Nuke
This commit is contained in:
commit
932413bb3d
61 changed files with 7249 additions and 0 deletions
355
.gitignore
vendored
Normal file
355
.gitignore
vendored
Normal file
|
@ -0,0 +1,355 @@
|
|||
### Project Specific stuff
|
||||
test_run_dir/*
|
||||
### XilinxISE template
|
||||
# intermediate build files
|
||||
*.bgn
|
||||
*.bit
|
||||
*.bld
|
||||
*.cmd_log
|
||||
*.drc
|
||||
*.ll
|
||||
*.lso
|
||||
*.msd
|
||||
*.msk
|
||||
*.ncd
|
||||
*.ngc
|
||||
*.ngd
|
||||
*.ngr
|
||||
*.pad
|
||||
*.par
|
||||
*.pcf
|
||||
*.prj
|
||||
*.ptwx
|
||||
*.rbb
|
||||
*.rbd
|
||||
*.stx
|
||||
*.syr
|
||||
*.twr
|
||||
*.twx
|
||||
*.unroutes
|
||||
*.ut
|
||||
*.xpi
|
||||
*.xst
|
||||
*_bitgen.xwbt
|
||||
*_envsettings.html
|
||||
*_map.map
|
||||
*_map.mrp
|
||||
*_map.ngm
|
||||
*_map.xrpt
|
||||
*_ngdbuild.xrpt
|
||||
*_pad.csv
|
||||
*_pad.txt
|
||||
*_par.xrpt
|
||||
*_summary.html
|
||||
*_summary.xml
|
||||
*_usage.xml
|
||||
*_xst.xrpt
|
||||
|
||||
# project-wide generated files
|
||||
*.gise
|
||||
par_usage_statistics.html
|
||||
usage_statistics_webtalk.html
|
||||
webtalk.log
|
||||
webtalk_pn.xml
|
||||
|
||||
# generated folders
|
||||
iseconfig/
|
||||
xlnx_auto_0_xdb/
|
||||
xst/
|
||||
_ngo/
|
||||
_xmsgs/
|
||||
### Eclipse template
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
### C template
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
### SBT template
|
||||
# Simple Build Tool
|
||||
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
|
||||
|
||||
target/
|
||||
lib_managed/
|
||||
src_managed/
|
||||
project/boot/
|
||||
.history
|
||||
.cache
|
||||
### Emacs template
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
### Vim template
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
*~
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
### C++ template
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
### OSX template
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
### Xcode template
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
### Scala template
|
||||
*.class
|
||||
*.log
|
||||
|
||||
# sbt specific
|
||||
.cache
|
||||
.history
|
||||
.lib/
|
||||
dist/*
|
||||
target/
|
||||
lib_managed/
|
||||
src_managed/
|
||||
project/boot/
|
||||
project/plugins/project/
|
||||
.ensime
|
||||
.ensime_cache/
|
||||
|
||||
# Scala-IDE specific
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
### Java template
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
*.fir
|
||||
*.json
|
||||
*.xml
|
||||
|
||||
# ENSIME, metals and friends
|
||||
.ensime*
|
||||
.metals*
|
||||
.bloop*
|
||||
.projectile
|
||||
target/
|
||||
scratchpad.scala
|
||||
log/
|
||||
index.html
|
31
README.md
Normal file
31
README.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
This is the coursework for the graded part of the TDT4255 course at NTNU.
|
||||
|
||||
Since it is the authors opinion that most tools out there are vastly underdesigned, this project comes
|
||||
with a lot of added homegrown utilities, including a RISC-V parser, assembler and interpreter.
|
||||
|
||||
When you test a design with a given program, that program is first parsed, then run in a software interpreter
|
||||
to get correct output, then assembled into a binary.
|
||||
This binary will then be loaded to your synthesized design (your processor) by the test harness provided in
|
||||
the skeleton code, along with any initial state.
|
||||
|
||||
Your processor will run the supplied binary, and the changes to state (memory and registers) will be recorded
|
||||
and compared with the interpreter log.
|
||||
|
||||
If it matches, your processor works, if not, you get an execution trace, hopefully showing what went wrong and
|
||||
where.
|
||||
|
||||
To get started, read the exercise.org file, it goes over the first pieces of the puzzle.
|
||||
|
||||
|
||||
If you want to learn chisel on your own and use this project please send me some feedback on what you liked,
|
||||
disliked and what could have been improved :)
|
||||
If you end up using it for a course you're teaching I would be thrilled too.
|
||||
In this case, you can spend the time you're saving by sending a pull requests with some improvements!
|
||||
|
||||
Pull requests are more than welcome!
|
||||
|
||||
Nice to have list:
|
||||
* More sophisticated test feedback. A detailed error report on why the processor design failed.
|
||||
* Scaffolding to run synthesized designs. Preferrably targeting the PYNQ platform.
|
||||
* A fix for whatever problems *you* run into when using this project.
|
||||
* Either a battery of tests to find corner cases stress testing forwarders, hazard detectors etc, or even better, the tools to generate code with hazards automatically.
|
49
TODO.org
Normal file
49
TODO.org
Normal file
|
@ -0,0 +1,49 @@
|
|||
* Tasks
|
||||
** DONE File IO and test
|
||||
** DONE Stop exploding the heap with logs :DDD
|
||||
** DONE Fix DONE instruction for VM termination
|
||||
*** DONE Add setting instructions
|
||||
** DONE Add assembler
|
||||
** DONE Chisel tester
|
||||
** DONE Add LF
|
||||
** DONE Redo colors in fansi. ANSI fucks up string formatting
|
||||
** DONE Columnize log events
|
||||
** DONE Chisel test log evaluator
|
||||
** DONE Create giftWrapper script
|
||||
** DONE Better sourceinfo stuff
|
||||
Good enough
|
||||
|
||||
** DONE Test options
|
||||
*** DONE How much NOP pad?
|
||||
*** DONE Verbosity?
|
||||
*** DONE Which tests?
|
||||
** DONE ish Step counter, pretty print VM log, including final memory state
|
||||
** TODO More programs
|
||||
*** DONE Real programs
|
||||
*** TODO Basic programs
|
||||
Needs more
|
||||
** DONE Merge in LF changes
|
||||
|
||||
** TODO Breakpoints
|
||||
*** TODO VM breakpoints
|
||||
**** TODO Record breakpoints in chisel tester
|
||||
*** TODO Chisel breakpoints
|
||||
**** TODO Freeze processor to record state
|
||||
**** TODO Record breakpoints in chisel tester
|
||||
*** TODO Draw breakpoints in the printer
|
||||
** TODO Calculate steps needed
|
||||
** TODO Unmangle derailed traces
|
||||
With incorrect designs the trace printer ends up printing a lot of diveregent
|
||||
unsychnronizable blocks
|
||||
** DONE Fix DONE instruction
|
||||
*** DONE Parse error
|
||||
*** DONE Use DONE address
|
||||
** DONE Hazard generator
|
||||
good enough
|
||||
|
||||
* Maybe
|
||||
** DONE Move instruction recording to IMEM rather than IF?
|
||||
Only care about what IF gets, won't have to deal with whatever logic is in IF.
|
||||
** DONE Figure out why loading instructions backwards made shit werk
|
||||
Not as funny as you'd think. The issue was overwriting the last written instruction with 0
|
||||
|
71
build.sbt
Normal file
71
build.sbt
Normal file
|
@ -0,0 +1,71 @@
|
|||
def scalacOptionsVersion(scalaVersion: String): Seq[String] = {
|
||||
Seq() ++ {
|
||||
// If we're building with Scala > 2.11, enable the compile option
|
||||
// switch to support our anonymous Bundle definitions:
|
||||
// https://github.com/scala/bug/issues/10047
|
||||
CrossVersion.partialVersion(scalaVersion) match {
|
||||
case Some((2, scalaMajor: Long)) if scalaMajor < 12 => Seq()
|
||||
case _ => Seq("-Xsource:2.11")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def javacOptionsVersion(scalaVersion: String): Seq[String] = {
|
||||
Seq() ++ {
|
||||
// Scala 2.12 requires Java 8. We continue to generate
|
||||
// Java 7 compatible code for Scala 2.11
|
||||
// for compatibility with old clients.
|
||||
CrossVersion.partialVersion(scalaVersion) match {
|
||||
case Some((2, scalaMajor: Long)) if scalaMajor < 12 =>
|
||||
Seq("-source", "1.7", "-target", "1.7")
|
||||
case _ =>
|
||||
Seq("-source", "1.8", "-target", "1.8")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name := "FiveStage"
|
||||
|
||||
version := "2.0.0"
|
||||
|
||||
scalaVersion := "2.12.8"
|
||||
|
||||
crossScalaVersions := Seq("2.11.12", "2.12.4")
|
||||
|
||||
resolvers ++= Seq(
|
||||
Resolver.sonatypeRepo("snapshots"),
|
||||
Resolver.sonatypeRepo("releases")
|
||||
)
|
||||
|
||||
// Provide a managed dependency on X if -DXVersion="" is supplied on the command line.
|
||||
val defaultVersions = Map(
|
||||
"chisel3" -> "3.1.+",
|
||||
"chisel-iotesters" -> "1.2.+"
|
||||
)
|
||||
|
||||
libraryDependencies ++= (Seq("chisel3","chisel-iotesters").map {
|
||||
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) })
|
||||
|
||||
val versionOfScala = "2.12.4"
|
||||
|
||||
val fs2Version = "0.10.3"
|
||||
val catsVersion = "1.1.0"
|
||||
val catsEffectVersion = "0.10"
|
||||
libraryDependencies ++= Dependencies.backendDeps.value
|
||||
scalacOptions ++= scalacOptionsVersion(scalaVersion.value)
|
||||
scalacOptions ++= Seq("-language:reflectiveCalls")
|
||||
scalacOptions ++= Seq("-Ypartial-unification")
|
||||
|
||||
javacOptions ++= javacOptionsVersion(scalaVersion.value)
|
||||
|
||||
// testOptions in Test += Tests.Argument("-oF")
|
||||
|
||||
resolvers += Resolver.sonatypeRepo("releases")
|
||||
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7")
|
||||
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.2.4")
|
||||
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
|
||||
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-eS")
|
||||
|
||||
addCompilerPlugin("io.tryp" % "splain" % "0.4.1" cross CrossVersion.patch)
|
25
deliver.sh
Executable file
25
deliver.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
read -p "Enter your student username (the one you use on badboard): " username
|
||||
echo "Cleaning your project"
|
||||
|
||||
./sbt.sh clean
|
||||
|
||||
echo "Creating archive"
|
||||
mkdir wrap
|
||||
cp -r src ./wrap/
|
||||
cp build.sbt ./wrap
|
||||
cp project/Dependencies.scala ./wrap/project/Dependencies.scala
|
||||
cp project/build.properties ./wrap/project/build.properties
|
||||
cp sbt.sh ./wrap
|
||||
tar czfv $username.gz wrap
|
||||
|
||||
rm -rf ./wrap
|
||||
|
||||
echo "Unwrapping and testing your wrapped package"
|
||||
mkdir wrapTest
|
||||
tar -C ./wrapTest -xvf $username.gz
|
||||
./wrapTest/wrap/sbt.sh test
|
||||
rm -rf ./wrapTest
|
||||
|
||||
echo "If the test output looked good then you're good to go!"
|
120
exercise.org
Normal file
120
exercise.org
Normal file
|
@ -0,0 +1,120 @@
|
|||
* Exercise 1 & 2
|
||||
The task in this exercise is to implement a 5-stage pipelined processor for
|
||||
the RISCV32I instruction set.
|
||||
|
||||
You will use the skeleton code which comes with a freebies, namely the registers,
|
||||
instruction memory and data memory.
|
||||
|
||||
These are contained in the files Registers.scala, Dmem.scala and Imem.scala
|
||||
|
||||
** Getting started
|
||||
In order to make a correct design in a somewhat expedient fashion you need to be
|
||||
*methodical!*
|
||||
|
||||
This means you should have a good idea of how your processor should work *before*
|
||||
you start writing code. While chisel is more pleasent to work with than other HDLs
|
||||
the bricoleur approach is not recommended.
|
||||
|
||||
My recommended approach is therefore to create a sketch of your processor design.
|
||||
Start with an overall sketch showing all the components, then drill down.
|
||||
In your sketch you will eventually add a box for registers, IMEM and DMEM, which
|
||||
should make it clear how the already finished modules fit into the grander design,
|
||||
making the skeleton-code less mysterious.
|
||||
|
||||
Next, your focus should be to get the simplest possible program to work, a program
|
||||
that simply does a single add operation. Info is progressively being omitted in the
|
||||
later steps, after all brevity is ~~the soul of~~ wit
|
||||
|
||||
Step 0:
|
||||
In order to verify that the project is set up properly, open sbt in your project root
|
||||
by typing ./sbt (or simply sbt if you already use scala).
|
||||
sbt, which stands for scala build tool will provide you with a repl where you can
|
||||
compile and test your code.
|
||||
|
||||
The initial run will take quite a while to boot as all the necessary stuff is downloaded.
|
||||
|
||||
Step ¼:
|
||||
In your console, type `compile` to verify that everything compiles correctly.
|
||||
|
||||
Step ½:
|
||||
In your console, type `test` to verify that the tests run, and that chisel can correctly
|
||||
build your design.
|
||||
This command will unleash the full battery of tests on you.
|
||||
|
||||
Step ¾:
|
||||
In your console, type `testOnly FiveStage.SelectedTests` to run only the tests that you
|
||||
have defined in the testConf.scala file.
|
||||
In the skeleton this will run the simple add test only, but you should alter this
|
||||
manifest as you build your processor to run more complex tests as a stopgap between
|
||||
running single tests and the full battery.
|
||||
|
||||
Be aware that chisel will make quite a lot of noise during test running. I'm not
|
||||
aware of a good way to get rid of this sadly.
|
||||
|
||||
Step 1:
|
||||
In order to do this, your processor must be able to select new instructions, so in
|
||||
your IF.scala you must increment the PC.
|
||||
|
||||
Step 2:
|
||||
Next, the instruction must be forwarded to the ID stage, so you will need to add the
|
||||
instruction to the io part of InstructionFetch as an output.
|
||||
|
||||
Step 3:
|
||||
Your ID stage must take in an instruction in its io bundle, and decode it. In the
|
||||
skeleton code a decoder has already been instantiated in the InstructionDecode module,
|
||||
but it is given a dummy instruction.
|
||||
Likewise, you must ensure that the register gets the relevant data.
|
||||
This can be done by using the instruction class methods (TopLevelSignals.scala) which
|
||||
lets us access the relevant part of the instruction with the dot operator.
|
||||
For instance:
|
||||
|
||||
#+BEGIN_SRC scala
|
||||
myModule.io.funct6 := io.instruction.funct6
|
||||
#+END_SRC
|
||||
|
||||
drives funct6 of `myModule` with the 26th to 31st bit of `instruction`.
|
||||
|
||||
Step 4:
|
||||
Your IF should now have an instruction as an OUTPUT, and your ID as an INPUT, however
|
||||
they are not connected. This must be done in the CPU class where both the ID and IF are
|
||||
instantiated.
|
||||
|
||||
Step 4½:
|
||||
You should now verify that the correct control signals are produced. Using printf, ensure
|
||||
that:
|
||||
+ The program counter is increasing in increments of 4
|
||||
+ The instruction in ID is as expected
|
||||
+ The decoder output is as expected
|
||||
+ The correct operands are fetched from the registers
|
||||
|
||||
Step 5:
|
||||
You will now have to create the EX stage. Use the structure of the IF and ID modules to
|
||||
guide you here.
|
||||
In your EX stage you should have an ALU, preferrable in its own module a la registers in ID.
|
||||
While the ALU is hugely complex, it's very easy to describle in hardware design languages!
|
||||
Using the same approach as in the decoder should be sufficient:
|
||||
|
||||
#+BEGIN_SRC scala
|
||||
val ALUopMap = Array(
|
||||
ADD -> (io.op1 + io.op2),
|
||||
SUB -> (io.op1 - io.op2),
|
||||
...
|
||||
)
|
||||
|
||||
io.aluResult := MuxLookup(0.U(32.W), io.aluOp, ALUopMap)
|
||||
#+END_SRC
|
||||
|
||||
Step 6:
|
||||
Your MEM stage does very little when an ADD instruction is executed, so implementing it should
|
||||
be easy
|
||||
|
||||
Step 7:
|
||||
You now need to actually write the result back to your register bank.
|
||||
This should be handled at the CPU level.
|
||||
If you sketched your processor already you probably made sure to keep track of the control
|
||||
signals for the instruction currently in WB, so writing to the correct register address should
|
||||
be easy for you ;)
|
||||
|
||||
Step 8:
|
||||
Ensure that the simplest add test works, give yourself a pat on the back, you've just found the
|
||||
corner pieces of the puzzle, so filling in the rest is "simply" being methodical.
|
449
instructions.org
Normal file
449
instructions.org
Normal file
|
@ -0,0 +1,449 @@
|
|||
--------------------------------------------------------------------------
|
||||
4.2. Register-Register Arithmetic Instructions
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
* ADD
|
||||
|
||||
- Summary : Addition with 3 GPRs, no overflow exception
|
||||
- Assembly : add rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] + R[rs2]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 000 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* SUB
|
||||
|
||||
- Summary : Subtraction with 3 GPRs, no overflow exception
|
||||
- Assembly : sub rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] - R[rs2]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0100000 | rs2 | rs1 | 000 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* AND
|
||||
|
||||
- Summary : Bitwise logical AND with 3 GPRs
|
||||
- Assembly : and rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] & R[rs2]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 111 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* OR
|
||||
|
||||
- Summary : Bitwise logical OR with 3 GPRs
|
||||
- Assembly : or rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] | R[rs2]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 110 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* XOR
|
||||
|
||||
- Summary : Bitwise logical XOR with 3 GPRs
|
||||
- Assembly : xor rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] ^ R[rs2]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 100 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* SLT
|
||||
|
||||
- Summary : Record result of signed less-than comparison with 2 GPRs
|
||||
- Assembly : slt rd, rs1, rs2
|
||||
- Semantics : R[rd] = ( R[rs1] <s R[rs2] )
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 010 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses a _signed_ comparison.
|
||||
|
||||
* SLTU
|
||||
|
||||
- Summary : Record result of unsigned less-than comparison with 2 GPRs
|
||||
- Assembly : sltu rd, rs1, rs2
|
||||
- Semantics : R[rd] = ( R[rs1] <u R[rs2] )
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 011 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses an _unsigned_ comparison.
|
||||
|
||||
* SRA
|
||||
|
||||
- Summary : Shift right arithmetic by register value (sign-extend)
|
||||
- Assembly : sra rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] >>> R[rs2][4:0]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0100000 | rs2 | rs1 | 101 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should ensure that the sign-bit of R[rs1] is
|
||||
extended to the right as it does the right shift. The hardware _must_
|
||||
only use the bottom five bits of R[rs2] when performing the shift.
|
||||
|
||||
* SRL
|
||||
|
||||
- Summary : Shift right logical by register value (append zeroes)
|
||||
- Assembly : srl rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] >> R[rs2][4:0]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 101 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should append zeros to the left as it does the
|
||||
right shift. The hardware _must_ only use the bottom five bits of R[rs2]
|
||||
when performing the shift.
|
||||
|
||||
* SLL
|
||||
|
||||
- Summary : Shift left logical by register value (append zeroes)
|
||||
- Assembly : sll rd, rs1, rs2
|
||||
- Semantics : R[rd] = R[rs1] << R[rs2][4:0]
|
||||
- Format : R-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | rs2 | rs1 | 001 | rd | 0110011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should append zeros to the right as it does the
|
||||
left shift. The hardware _must_ only use the bottom five bits of R[rs2]
|
||||
when performing the shift.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
4.3. Register-Immediate Arithmetic Instructions
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
* ADDI
|
||||
|
||||
- Summary : Add constant, no overflow exception
|
||||
- Assembly : addi rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] + sext(imm)
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 000 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* ANDI
|
||||
|
||||
- Summary : Bitwise logical AND with constant
|
||||
- Assembly : andi rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] & sext(imm)
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 111 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* ORI
|
||||
|
||||
- Summary : Bitwise logical OR with constant
|
||||
- Assembly : ori rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] | sext(imm)
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 110 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* XORI
|
||||
|
||||
- Summary : Bitwise logical XOR with constant
|
||||
- Assembly : xori rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] ^ sext(imm)
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 100 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* SLTI
|
||||
|
||||
- Summary : Set GPR if source GPR < constant, signed comparison
|
||||
- Assembly : slti rd, rs1, imm
|
||||
- Semantics : R[rd] = ( R[rs1] <s sext(imm) )
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 010 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* SLTIU
|
||||
|
||||
- Summary : Set GPR if source GPR is < constant, unsigned comparison
|
||||
- Assembly : sltiu rd, rs1, imm
|
||||
- Semantics : R[rd] = ( R[rs1] <u sext(imm) )
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 011 | rd | 0010011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
* SRAI
|
||||
|
||||
- Summary : Shift right arithmetic by constant (sign-extend)
|
||||
- Assembly : srai rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] >>> imm
|
||||
- Format : I-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0100000 | imm | rs1 | 101 | rd | 0010011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should ensure that the sign-bit of R[rs1] is
|
||||
extended to the right as it does the right shift.
|
||||
|
||||
* SRLI
|
||||
|
||||
- Summary : Shift right logical by constant (append zeroes)
|
||||
- Assembly : srli rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] >> imm
|
||||
- Format : I-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | imm | rs1 | 101 | rd | 0010011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should append zeros to the left as it does the
|
||||
right shift.
|
||||
|
||||
* SLLI
|
||||
|
||||
- Summary : Shift left logical constant (append zeroes)
|
||||
- Assembly : slli rd, rs1, imm
|
||||
- Semantics : R[rd] = R[rs1] << imm
|
||||
- Format : I-type
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| 0000000 | imm | rs1 | 001 | rd | 0010011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
Note that the hardware should append zeros to the right as it does the
|
||||
left shift.
|
||||
|
||||
* LUI
|
||||
|
||||
- Summary : Load constant into upper bits of word
|
||||
- Assembly : lui rd, imm
|
||||
- Semantics : R[rd] = imm << 12
|
||||
- Format : U-type, U-immediate
|
||||
|
||||
31 11 7 6 0
|
||||
+---------------------------------------+---------+-------------+
|
||||
| imm | rd | 0110111 |
|
||||
+---------------------------------------+---------+-------------+
|
||||
|
||||
* AUIPC
|
||||
|
||||
- Summary : Load PC + constant into upper bits of word
|
||||
- Assembly : auipc rd, imm
|
||||
- Semantics : R[rd] = PC + ( imm << 12 )
|
||||
- Format : U-type, U-immediate
|
||||
|
||||
31 11 7 6 0
|
||||
+---------------------------------------+---------+-------------+
|
||||
| imm | rd | 0010111 |
|
||||
+---------------------------------------+---------+-------------+
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
4.4. Memory Instructions
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
* LW
|
||||
|
||||
- Summary : Load word from memory
|
||||
- Assembly : lw rd, imm(rs1)
|
||||
- Semantics : R[rd] = M_4B[ R[rs1] + sext(imm) ]
|
||||
- Format : I-type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 010 | rd | 0000011 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
All addresses used with LW instructions must be four-byte aligned. This
|
||||
means the bottom two bits of every effective address (i.e., after the
|
||||
base address is added to the offset) will always be zero.
|
||||
|
||||
* SW
|
||||
|
||||
- Summary : Store word into memory
|
||||
- Assembly : sw rs2, imm(rs1)
|
||||
- Semantics : M_4B[ R[rs1] + sext(imm) ] = R[rs2]
|
||||
- Format : S-type, S-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 010 | imm | 0100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
All addresses used with SW instructions must be four-byte aligned. This
|
||||
means the bottom two bits of every effective address (i.e., after the
|
||||
base address is added to the offset) will always be zero.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
4.5. Unconditional Jump Instructions
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
* JAL
|
||||
|
||||
- Summary : Jump to address and place return address in GPR
|
||||
- Assembly : jal rd, imm
|
||||
- Semantics : R[rd] = PC + 4; PC = PC + sext(imm)
|
||||
- Format : U-type, J-immediate
|
||||
|
||||
31 11 7 6 0
|
||||
+---------------------------------------+---------+-------------+
|
||||
| imm | rd | 1101111 |
|
||||
+---------------------------------------+---------+-------------+
|
||||
|
||||
|
||||
* JALR
|
||||
|
||||
- Summary : Jump to address and place return address in GPR
|
||||
- Assembly : jalr rd, rs1, imm
|
||||
- Semantics : R[rd] = PC + 4; PC = ( R[rs1] + sext(imm) ) & 0xfffffffe
|
||||
- Format : I-Type, I-immediate
|
||||
|
||||
31 20 19 15 14 12 11 7 6 0
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
| imm | rs1 | 000 | rd | 1100111 |
|
||||
+----------------------+---------+------+---------+-------------+
|
||||
|
||||
Note that the target address is obtained by adding the 12-bit signed
|
||||
I-immediate to the value in register rs1, then setting the
|
||||
least-significant bit of the result to zero. In other words, the JALR
|
||||
instruction ignores the lowest bit of the calculated target address.
|
||||
|
||||
JALR is used when we want to call different subroutines.
|
||||
Consider this jump table:
|
||||
|
||||
mul:
|
||||
j mulInt
|
||||
j mulFloat
|
||||
j mulDouble
|
||||
|
||||
depending on the value in rs1 we can select which subroutine to call
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
4.6. Conditional Branch Instructions
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
* BEQ
|
||||
|
||||
- Summary : Branch if 2 GPRs are equal
|
||||
- Assembly : beq rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] == R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 000 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* BNE
|
||||
|
||||
- Summary : Branch if 2 GPRs are not equal
|
||||
- Assembly : bne rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] != R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 001 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
* BLT
|
||||
|
||||
- Summary : Branch based on signed comparison of two GPRs
|
||||
- Assembly : blt rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] <s R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 100 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses a _signed_ comparison.
|
||||
|
||||
* BGE
|
||||
|
||||
- Summary : Branch based on signed comparison of two GPRs
|
||||
- Assembly : bge rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] >=s R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 101 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses a _signed_ comparison.
|
||||
|
||||
* BLTU
|
||||
|
||||
- Summary : Branch based on unsigned comparison of two GPRs
|
||||
- Assembly : bltu rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] <u R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 110 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses an _unsigned_ comparison.
|
||||
|
||||
* BGEU
|
||||
|
||||
- Summary : Branch based on unsigned comparison of two GPRs
|
||||
- Assembly : bgeu rs1, rs2, imm
|
||||
- Semantics : PC = ( R[rs1] >=u R[rs2] ) ? PC + sext(imm) : PC + 4
|
||||
- Format : S-type, B-immediate
|
||||
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
| imm | rs2 | rs1 | 111 | imm | 1100011 |
|
||||
+------------+---------+---------+------+---------+-------------+
|
||||
|
||||
This instruction uses an _unsigned_ comparison.
|
30
project/Dependencies.scala
Normal file
30
project/Dependencies.scala
Normal file
|
@ -0,0 +1,30 @@
|
|||
import sbt._
|
||||
|
||||
object Dependencies {
|
||||
|
||||
val fs2Version = "1.0.0"
|
||||
val catsVersion = "1.4.0"
|
||||
val catsEffectVersion = "1.0.0"
|
||||
|
||||
// TODO prune deps
|
||||
val backendDeps = Def.setting(
|
||||
Seq(
|
||||
"com.lihaoyi" %% "sourcecode" % "0.1.4", // expert println debugging
|
||||
"com.lihaoyi" %% "pprint" % "0.5.3", // pretty print for types and case classes
|
||||
"com.lihaoyi" %% "fansi" % "0.2.5",
|
||||
|
||||
"org.typelevel" %% "cats-core" % catsVersion, // abstract category dork stuff
|
||||
"org.typelevel" %% "cats-effect" % catsEffectVersion, // IO monad category wank
|
||||
|
||||
"com.chuusai" %% "shapeless" % "2.3.2", // Abstract level category dork stuff
|
||||
|
||||
"org.tpolecat" %% "atto-core" % "0.6.3", // For parsing asm
|
||||
"org.tpolecat" %% "atto-refined" % "0.6.3", // For parsing asm
|
||||
|
||||
"com.github.pathikrit" %% "better-files" % "3.7.0",
|
||||
|
||||
"org.atnos" %% "eff" % "5.2.0",
|
||||
"com.slamdata" %% "matryoshka-core" % "0.21.3"
|
||||
|
||||
))
|
||||
}
|
1
project/build.properties
Normal file
1
project/build.properties
Normal file
|
@ -0,0 +1 @@
|
|||
sbt.version = 1.1.0
|
578
sbt.sh
Executable file
578
sbt.sh
Executable file
|
@ -0,0 +1,578 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# A more capable sbt runner, coincidentally also called sbt.
|
||||
# Author: Paul Phillips <paulp@improving.org>
|
||||
# https://github.com/paulp/sbt-extras
|
||||
|
||||
set -o pipefail
|
||||
|
||||
declare -r sbt_release_version="1.2.8"
|
||||
declare -r sbt_unreleased_version="1.2.8"
|
||||
|
||||
declare -r latest_213="2.13.0-RC1"
|
||||
declare -r latest_212="2.12.8"
|
||||
declare -r latest_211="2.11.12"
|
||||
declare -r latest_210="2.10.7"
|
||||
declare -r latest_29="2.9.3"
|
||||
declare -r latest_28="2.8.2"
|
||||
|
||||
declare -r buildProps="project/build.properties"
|
||||
|
||||
declare -r sbt_launch_ivy_release_repo="http://repo.typesafe.com/typesafe/ivy-releases"
|
||||
declare -r sbt_launch_ivy_snapshot_repo="https://repo.scala-sbt.org/scalasbt/ivy-snapshots"
|
||||
declare -r sbt_launch_mvn_release_repo="http://repo.scala-sbt.org/scalasbt/maven-releases"
|
||||
declare -r sbt_launch_mvn_snapshot_repo="http://repo.scala-sbt.org/scalasbt/maven-snapshots"
|
||||
|
||||
declare -r default_jvm_opts_common="-Xms512m -Xss2m"
|
||||
declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy"
|
||||
|
||||
declare sbt_jar sbt_dir sbt_create sbt_version sbt_script sbt_new
|
||||
declare sbt_explicit_version
|
||||
declare verbose noshare batch trace_level
|
||||
declare debugUs
|
||||
|
||||
declare java_cmd="java"
|
||||
declare sbt_launch_dir="$HOME/.sbt/launchers"
|
||||
declare sbt_launch_repo
|
||||
|
||||
# pull -J and -D options to give to java.
|
||||
declare -a java_args scalac_args sbt_commands residual_args
|
||||
|
||||
# args to jvm/sbt via files or environment variables
|
||||
declare -a extra_jvm_opts extra_sbt_opts
|
||||
|
||||
echoerr () { echo >&2 "$@"; }
|
||||
vlog () { [[ -n "$verbose" ]] && echoerr "$@"; }
|
||||
die () { echo "Aborting: $*" ; exit 1; }
|
||||
|
||||
setTrapExit () {
|
||||
# save stty and trap exit, to ensure echo is re-enabled if we are interrupted.
|
||||
SBT_STTY="$(stty -g 2>/dev/null)"
|
||||
export SBT_STTY
|
||||
|
||||
# restore stty settings (echo in particular)
|
||||
onSbtRunnerExit() {
|
||||
[ -t 0 ] || return
|
||||
vlog ""
|
||||
vlog "restoring stty: $SBT_STTY"
|
||||
stty "$SBT_STTY"
|
||||
}
|
||||
|
||||
vlog "saving stty: $SBT_STTY"
|
||||
trap onSbtRunnerExit EXIT
|
||||
}
|
||||
|
||||
# this seems to cover the bases on OSX, and someone will
|
||||
# have to tell me about the others.
|
||||
get_script_path () {
|
||||
local path="$1"
|
||||
[[ -L "$path" ]] || { echo "$path" ; return; }
|
||||
|
||||
local -r target="$(readlink "$path")"
|
||||
if [[ "${target:0:1}" == "/" ]]; then
|
||||
echo "$target"
|
||||
else
|
||||
echo "${path%/*}/$target"
|
||||
fi
|
||||
}
|
||||
|
||||
script_path="$(get_script_path "${BASH_SOURCE[0]}")"
|
||||
declare -r script_path
|
||||
script_name="${script_path##*/}"
|
||||
declare -r script_name
|
||||
|
||||
init_default_option_file () {
|
||||
local overriding_var="${!1}"
|
||||
local default_file="$2"
|
||||
if [[ ! -r "$default_file" && "$overriding_var" =~ ^@(.*)$ ]]; then
|
||||
local envvar_file="${BASH_REMATCH[1]}"
|
||||
if [[ -r "$envvar_file" ]]; then
|
||||
default_file="$envvar_file"
|
||||
fi
|
||||
fi
|
||||
echo "$default_file"
|
||||
}
|
||||
|
||||
sbt_opts_file="$(init_default_option_file SBT_OPTS .sbtopts)"
|
||||
jvm_opts_file="$(init_default_option_file JVM_OPTS .jvmopts)"
|
||||
|
||||
build_props_sbt () {
|
||||
[[ -r "$buildProps" ]] && \
|
||||
grep '^sbt\.version' "$buildProps" | tr '=\r' ' ' | awk '{ print $2; }'
|
||||
}
|
||||
|
||||
set_sbt_version () {
|
||||
sbt_version="${sbt_explicit_version:-$(build_props_sbt)}"
|
||||
[[ -n "$sbt_version" ]] || sbt_version=$sbt_release_version
|
||||
export sbt_version
|
||||
}
|
||||
|
||||
url_base () {
|
||||
local version="$1"
|
||||
|
||||
case "$version" in
|
||||
0.7.*) echo "http://simple-build-tool.googlecode.com" ;;
|
||||
0.10.* ) echo "$sbt_launch_ivy_release_repo" ;;
|
||||
0.11.[12]) echo "$sbt_launch_ivy_release_repo" ;;
|
||||
0.*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmdd-hhMMss"
|
||||
echo "$sbt_launch_ivy_snapshot_repo" ;;
|
||||
0.*) echo "$sbt_launch_ivy_release_repo" ;;
|
||||
*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmdd-hhMMss"
|
||||
echo "$sbt_launch_mvn_snapshot_repo" ;;
|
||||
*) echo "$sbt_launch_mvn_release_repo" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
make_url () {
|
||||
local version="$1"
|
||||
|
||||
local base="${sbt_launch_repo:-$(url_base "$version")}"
|
||||
|
||||
case "$version" in
|
||||
0.7.*) echo "$base/files/sbt-launch-0.7.7.jar" ;;
|
||||
0.10.* ) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;;
|
||||
0.11.[12]) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;;
|
||||
0.*) echo "$base/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" ;;
|
||||
*) echo "$base/org/scala-sbt/sbt-launch/$version/sbt-launch.jar" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
addJava () { vlog "[addJava] arg = '$1'" ; java_args+=("$1"); }
|
||||
addSbt () { vlog "[addSbt] arg = '$1'" ; sbt_commands+=("$1"); }
|
||||
addScalac () { vlog "[addScalac] arg = '$1'" ; scalac_args+=("$1"); }
|
||||
addResidual () { vlog "[residual] arg = '$1'" ; residual_args+=("$1"); }
|
||||
|
||||
addResolver () { addSbt "set resolvers += $1"; }
|
||||
addDebugger () { addJava "-Xdebug" ; addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"; }
|
||||
setThisBuild () {
|
||||
vlog "[addBuild] args = '$*'"
|
||||
local key="$1" && shift
|
||||
addSbt "set $key in ThisBuild := $*"
|
||||
}
|
||||
setScalaVersion () {
|
||||
[[ "$1" == *"-SNAPSHOT" ]] && addResolver 'Resolver.sonatypeRepo("snapshots")'
|
||||
addSbt "++ $1"
|
||||
}
|
||||
setJavaHome () {
|
||||
java_cmd="$1/bin/java"
|
||||
setThisBuild javaHome "_root_.scala.Some(file(\"$1\"))"
|
||||
export JAVA_HOME="$1"
|
||||
export JDK_HOME="$1"
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
}
|
||||
|
||||
getJavaVersion() {
|
||||
local -r str=$("$1" -version 2>&1 | grep -E -e '(java|openjdk) version' | awk '{ print $3 }' | tr -d '"')
|
||||
|
||||
# java -version on java8 says 1.8.x
|
||||
# but on 9 and 10 it's 9.x.y and 10.x.y.
|
||||
if [[ "$str" =~ ^1\.([0-9]+)\..*$ ]]; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
elif [[ "$str" =~ ^([0-9]+)\..*$ ]]; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
elif [[ -n "$str" ]]; then
|
||||
echoerr "Can't parse java version from: $str"
|
||||
fi
|
||||
}
|
||||
|
||||
checkJava() {
|
||||
# Warn if there is a Java version mismatch between PATH and JAVA_HOME/JDK_HOME
|
||||
|
||||
[[ -n "$JAVA_HOME" && -e "$JAVA_HOME/bin/java" ]] && java="$JAVA_HOME/bin/java"
|
||||
[[ -n "$JDK_HOME" && -e "$JDK_HOME/lib/tools.jar" ]] && java="$JDK_HOME/bin/java"
|
||||
|
||||
if [[ -n "$java" ]]; then
|
||||
pathJavaVersion=$(getJavaVersion java)
|
||||
homeJavaVersion=$(getJavaVersion "$java")
|
||||
if [[ "$pathJavaVersion" != "$homeJavaVersion" ]]; then
|
||||
echoerr "Warning: Java version mismatch between PATH and JAVA_HOME/JDK_HOME, sbt will use the one in PATH"
|
||||
echoerr " Either: fix your PATH, remove JAVA_HOME/JDK_HOME or use -java-home"
|
||||
echoerr " java version from PATH: $pathJavaVersion"
|
||||
echoerr " java version from JAVA_HOME/JDK_HOME: $homeJavaVersion"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
java_version () {
|
||||
local -r version=$(getJavaVersion "$java_cmd")
|
||||
vlog "Detected Java version: $version"
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# MaxPermSize critical on pre-8 JVMs but incurs noisy warning on 8+
|
||||
default_jvm_opts () {
|
||||
local -r v="$(java_version)"
|
||||
if [[ $v -ge 8 ]]; then
|
||||
echo "$default_jvm_opts_common"
|
||||
else
|
||||
echo "-XX:MaxPermSize=384m $default_jvm_opts_common"
|
||||
fi
|
||||
}
|
||||
|
||||
build_props_scala () {
|
||||
if [[ -r "$buildProps" ]]; then
|
||||
versionLine="$(grep '^build.scala.versions' "$buildProps")"
|
||||
versionString="${versionLine##build.scala.versions=}"
|
||||
echo "${versionString%% .*}"
|
||||
fi
|
||||
}
|
||||
|
||||
execRunner () {
|
||||
# print the arguments one to a line, quoting any containing spaces
|
||||
vlog "# Executing command line:" && {
|
||||
for arg; do
|
||||
if [[ -n "$arg" ]]; then
|
||||
if printf "%s\n" "$arg" | grep -q ' '; then
|
||||
printf >&2 "\"%s\"\n" "$arg"
|
||||
else
|
||||
printf >&2 "%s\n" "$arg"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
vlog ""
|
||||
}
|
||||
|
||||
setTrapExit
|
||||
|
||||
if [[ -n "$batch" ]]; then
|
||||
"$@" < /dev/null
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
jar_url () { make_url "$1"; }
|
||||
|
||||
is_cygwin () { [[ "$(uname -a)" == "CYGWIN"* ]]; }
|
||||
|
||||
jar_file () {
|
||||
is_cygwin \
|
||||
&& cygpath -w "$sbt_launch_dir/$1/sbt-launch.jar" \
|
||||
|| echo "$sbt_launch_dir/$1/sbt-launch.jar"
|
||||
}
|
||||
|
||||
download_url () {
|
||||
local url="$1"
|
||||
local jar="$2"
|
||||
|
||||
echoerr "Downloading sbt launcher for $sbt_version:"
|
||||
echoerr " From $url"
|
||||
echoerr " To $jar"
|
||||
|
||||
mkdir -p "${jar%/*}" && {
|
||||
if command -v curl > /dev/null 2>&1; then
|
||||
curl --fail --silent --location "$url" --output "$jar"
|
||||
elif command -v wget > /dev/null 2>&1; then
|
||||
wget -q -O "$jar" "$url"
|
||||
fi
|
||||
} && [[ -r "$jar" ]]
|
||||
}
|
||||
|
||||
acquire_sbt_jar () {
|
||||
{
|
||||
sbt_jar="$(jar_file "$sbt_version")"
|
||||
[[ -r "$sbt_jar" ]]
|
||||
} || {
|
||||
sbt_jar="$HOME/.ivy2/local/org.scala-sbt/sbt-launch/$sbt_version/jars/sbt-launch.jar"
|
||||
[[ -r "$sbt_jar" ]]
|
||||
} || {
|
||||
sbt_jar="$(jar_file "$sbt_version")"
|
||||
download_url "$(make_url "$sbt_version")" "$sbt_jar"
|
||||
}
|
||||
}
|
||||
|
||||
usage () {
|
||||
set_sbt_version
|
||||
cat <<EOM
|
||||
Usage: $script_name [options]
|
||||
|
||||
Note that options which are passed along to sbt begin with -- whereas
|
||||
options to this runner use a single dash. Any sbt command can be scheduled
|
||||
to run first by prefixing the command with --, so --warn, --error and so on
|
||||
are not special.
|
||||
|
||||
Output filtering: if there is a file in the home directory called .sbtignore
|
||||
and this is not an interactive sbt session, the file is treated as a list of
|
||||
bash regular expressions. Output lines which match any regex are not echoed.
|
||||
One can see exactly which lines would have been suppressed by starting this
|
||||
runner with the -x option.
|
||||
|
||||
-h | -help print this message
|
||||
-v verbose operation (this runner is chattier)
|
||||
-d, -w, -q aliases for --debug, --warn, --error (q means quiet)
|
||||
-x debug this script
|
||||
-trace <level> display stack traces with a max of <level> frames (default: -1, traces suppressed)
|
||||
-debug-inc enable debugging log for the incremental compiler
|
||||
-no-colors disable ANSI color codes
|
||||
-sbt-create start sbt even if current directory contains no sbt project
|
||||
-sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt/<version>)
|
||||
-sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11+)
|
||||
-ivy <path> path to local Ivy repository (default: ~/.ivy2)
|
||||
-no-share use all local caches; no sharing
|
||||
-offline put sbt in offline mode
|
||||
-jvm-debug <port> Turn on JVM debugging, open at the given port.
|
||||
-batch Disable interactive mode
|
||||
-prompt <expr> Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted
|
||||
-script <file> Run the specified file as a scala script
|
||||
|
||||
# sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version)
|
||||
-sbt-force-latest force the use of the latest release of sbt: $sbt_release_version
|
||||
-sbt-version <version> use the specified version of sbt (default: $sbt_release_version)
|
||||
-sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version
|
||||
-sbt-jar <path> use the specified jar as the sbt launcher
|
||||
-sbt-launch-dir <path> directory to hold sbt launchers (default: $sbt_launch_dir)
|
||||
-sbt-launch-repo <url> repo url for downloading sbt launcher jar (default: $(url_base "$sbt_version"))
|
||||
|
||||
# scala version (default: as chosen by sbt)
|
||||
-28 use $latest_28
|
||||
-29 use $latest_29
|
||||
-210 use $latest_210
|
||||
-211 use $latest_211
|
||||
-212 use $latest_212
|
||||
-213 use $latest_213
|
||||
-scala-home <path> use the scala build at the specified directory
|
||||
-scala-version <version> use the specified version of scala
|
||||
-binary-version <version> use the specified scala version when searching for dependencies
|
||||
|
||||
# java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
|
||||
-java-home <path> alternate JAVA_HOME
|
||||
|
||||
# passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution
|
||||
# The default set is used if JVM_OPTS is unset and no -jvm-opts file is found
|
||||
<default> $(default_jvm_opts)
|
||||
JVM_OPTS environment variable holding either the jvm args directly, or
|
||||
the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts')
|
||||
Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument.
|
||||
-jvm-opts <path> file containing jvm args (if not given, .jvmopts in project root is used if present)
|
||||
-Dkey=val pass -Dkey=val directly to the jvm
|
||||
-J-X pass option -X directly to the jvm (-J is stripped)
|
||||
|
||||
# passing options to sbt, OR to this runner
|
||||
SBT_OPTS environment variable holding either the sbt args directly, or
|
||||
the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts')
|
||||
Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument.
|
||||
-sbt-opts <path> file containing sbt args (if not given, .sbtopts in project root is used if present)
|
||||
-S-X add -X to sbt's scalacOptions (-S is stripped)
|
||||
EOM
|
||||
}
|
||||
|
||||
process_args () {
|
||||
require_arg () {
|
||||
local type="$1"
|
||||
local opt="$2"
|
||||
local arg="$3"
|
||||
|
||||
if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
|
||||
die "$opt requires <$type> argument"
|
||||
fi
|
||||
}
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|-help) usage; exit 0 ;;
|
||||
-v) verbose=true && shift ;;
|
||||
-d) addSbt "--debug" && shift ;;
|
||||
-w) addSbt "--warn" && shift ;;
|
||||
-q) addSbt "--error" && shift ;;
|
||||
-x) debugUs=true && shift ;;
|
||||
-trace) require_arg integer "$1" "$2" && trace_level="$2" && shift 2 ;;
|
||||
-ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;;
|
||||
-no-colors) addJava "-Dsbt.log.noformat=true" && shift ;;
|
||||
-no-share) noshare=true && shift ;;
|
||||
-sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;;
|
||||
-sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;;
|
||||
-debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;;
|
||||
-offline) addSbt "set offline in Global := true" && shift ;;
|
||||
-jvm-debug) require_arg port "$1" "$2" && addDebugger "$2" && shift 2 ;;
|
||||
-batch) batch=true && shift ;;
|
||||
-prompt) require_arg "expr" "$1" "$2" && setThisBuild shellPrompt "(s => { val e = Project.extract(s) ; $2 })" && shift 2 ;;
|
||||
-script) require_arg file "$1" "$2" && sbt_script="$2" && addJava "-Dsbt.main.class=sbt.ScriptMain" && shift 2 ;;
|
||||
|
||||
-sbt-create) sbt_create=true && shift ;;
|
||||
-sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;;
|
||||
-sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;;
|
||||
-sbt-force-latest) sbt_explicit_version="$sbt_release_version" && shift ;;
|
||||
-sbt-dev) sbt_explicit_version="$sbt_unreleased_version" && shift ;;
|
||||
-sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;;
|
||||
-sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;;
|
||||
-scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;;
|
||||
-binary-version) require_arg version "$1" "$2" && setThisBuild scalaBinaryVersion "\"$2\"" && shift 2 ;;
|
||||
-scala-home) require_arg path "$1" "$2" && setThisBuild scalaHome "_root_.scala.Some(file(\"$2\"))" && shift 2 ;;
|
||||
-java-home) require_arg path "$1" "$2" && setJavaHome "$2" && shift 2 ;;
|
||||
-sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;;
|
||||
-jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;;
|
||||
|
||||
-D*) addJava "$1" && shift ;;
|
||||
-J*) addJava "${1:2}" && shift ;;
|
||||
-S*) addScalac "${1:2}" && shift ;;
|
||||
-28) setScalaVersion "$latest_28" && shift ;;
|
||||
-29) setScalaVersion "$latest_29" && shift ;;
|
||||
-210) setScalaVersion "$latest_210" && shift ;;
|
||||
-211) setScalaVersion "$latest_211" && shift ;;
|
||||
-212) setScalaVersion "$latest_212" && shift ;;
|
||||
-213) setScalaVersion "$latest_213" && shift ;;
|
||||
new) sbt_new=true && : ${sbt_explicit_version:=$sbt_release_version} && addResidual "$1" && shift ;;
|
||||
*) addResidual "$1" && shift ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# process the direct command line arguments
|
||||
process_args "$@"
|
||||
|
||||
# skip #-styled comments and blank lines
|
||||
readConfigFile() {
|
||||
local end=false
|
||||
until $end; do
|
||||
read -r || end=true
|
||||
[[ $REPLY =~ ^# ]] || [[ -z $REPLY ]] || echo "$REPLY"
|
||||
done < "$1"
|
||||
}
|
||||
|
||||
# if there are file/environment sbt_opts, process again so we
|
||||
# can supply args to this runner
|
||||
if [[ -r "$sbt_opts_file" ]]; then
|
||||
vlog "Using sbt options defined in file $sbt_opts_file"
|
||||
while read -r opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbt_opts_file")
|
||||
elif [[ -n "$SBT_OPTS" && ! ("$SBT_OPTS" =~ ^@.*) ]]; then
|
||||
vlog "Using sbt options defined in variable \$SBT_OPTS"
|
||||
IFS=" " read -r -a extra_sbt_opts <<< "$SBT_OPTS"
|
||||
else
|
||||
vlog "No extra sbt options have been defined"
|
||||
fi
|
||||
|
||||
[[ -n "${extra_sbt_opts[*]}" ]] && process_args "${extra_sbt_opts[@]}"
|
||||
|
||||
# reset "$@" to the residual args
|
||||
set -- "${residual_args[@]}"
|
||||
argumentCount=$#
|
||||
|
||||
# set sbt version
|
||||
set_sbt_version
|
||||
|
||||
checkJava
|
||||
|
||||
# only exists in 0.12+
|
||||
setTraceLevel() {
|
||||
case "$sbt_version" in
|
||||
"0.7."* | "0.10."* | "0.11."* ) echoerr "Cannot set trace level in sbt version $sbt_version" ;;
|
||||
*) setThisBuild traceLevel "$trace_level" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# set scalacOptions if we were given any -S opts
|
||||
[[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[*]}\""
|
||||
|
||||
[[ -n "$sbt_explicit_version" && -z "$sbt_new" ]] && addJava "-Dsbt.version=$sbt_explicit_version"
|
||||
vlog "Detected sbt version $sbt_version"
|
||||
|
||||
if [[ -n "$sbt_script" ]]; then
|
||||
residual_args=( "$sbt_script" "${residual_args[@]}" )
|
||||
else
|
||||
# no args - alert them there's stuff in here
|
||||
(( argumentCount > 0 )) || {
|
||||
vlog "Starting $script_name: invoke with -help for other options"
|
||||
residual_args=( shell )
|
||||
}
|
||||
fi
|
||||
|
||||
# verify this is an sbt dir, -create was given or user attempts to run a scala script
|
||||
[[ -r ./build.sbt || -d ./project || -n "$sbt_create" || -n "$sbt_script" || -n "$sbt_new" ]] || {
|
||||
cat <<EOM
|
||||
$(pwd) doesn't appear to be an sbt project.
|
||||
If you want to start sbt anyway, run:
|
||||
$0 -sbt-create
|
||||
|
||||
EOM
|
||||
exit 1
|
||||
}
|
||||
|
||||
# pick up completion if present; todo
|
||||
# shellcheck disable=SC1091
|
||||
[[ -r .sbt_completion.sh ]] && source .sbt_completion.sh
|
||||
|
||||
# directory to store sbt launchers
|
||||
[[ -d "$sbt_launch_dir" ]] || mkdir -p "$sbt_launch_dir"
|
||||
[[ -w "$sbt_launch_dir" ]] || sbt_launch_dir="$(mktemp -d -t sbt_extras_launchers.XXXXXX)"
|
||||
|
||||
# no jar? download it.
|
||||
[[ -r "$sbt_jar" ]] || acquire_sbt_jar || {
|
||||
# still no jar? uh-oh.
|
||||
echo "Download failed. Obtain the jar manually and place it at $sbt_jar"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ -n "$noshare" ]]; then
|
||||
for opt in ${noshare_opts}; do
|
||||
addJava "$opt"
|
||||
done
|
||||
else
|
||||
case "$sbt_version" in
|
||||
"0.7."* | "0.10."* | "0.11."* | "0.12."* )
|
||||
[[ -n "$sbt_dir" ]] || {
|
||||
sbt_dir="$HOME/.sbt/$sbt_version"
|
||||
vlog "Using $sbt_dir as sbt dir, -sbt-dir to override."
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$sbt_dir" ]]; then
|
||||
addJava "-Dsbt.global.base=$sbt_dir"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -r "$jvm_opts_file" ]]; then
|
||||
vlog "Using jvm options defined in file $jvm_opts_file"
|
||||
while read -r opt; do extra_jvm_opts+=("$opt"); done < <(readConfigFile "$jvm_opts_file")
|
||||
elif [[ -n "$JVM_OPTS" && ! ("$JVM_OPTS" =~ ^@.*) ]]; then
|
||||
vlog "Using jvm options defined in \$JVM_OPTS variable"
|
||||
IFS=" " read -r -a extra_jvm_opts <<< "$JVM_OPTS"
|
||||
else
|
||||
vlog "Using default jvm options"
|
||||
IFS=" " read -r -a extra_jvm_opts <<< "$(default_jvm_opts)"
|
||||
fi
|
||||
|
||||
# traceLevel is 0.12+
|
||||
[[ -n "$trace_level" ]] && setTraceLevel
|
||||
|
||||
main () {
|
||||
execRunner "$java_cmd" \
|
||||
"${extra_jvm_opts[@]}" \
|
||||
"${java_args[@]}" \
|
||||
-jar "$sbt_jar" \
|
||||
"${sbt_commands[@]}" \
|
||||
"${residual_args[@]}"
|
||||
}
|
||||
|
||||
# sbt inserts this string on certain lines when formatting is enabled:
|
||||
# val OverwriteLine = "\r\u001BM\u001B[2K"
|
||||
# ...in order not to spam the console with a million "Resolving" lines.
|
||||
# Unfortunately that makes it that much harder to work with when
|
||||
# we're not going to print those lines anyway. We strip that bit of
|
||||
# line noise, but leave the other codes to preserve color.
|
||||
mainFiltered () {
|
||||
local -r excludeRegex=$(grep -E -v '^#|^$' ~/.sbtignore | paste -sd'|' -)
|
||||
|
||||
echoLine () {
|
||||
local -r line="$1"
|
||||
local -r line1="${line//\r\x1BM\x1B\[2K//g}" # This strips the OverwriteLine code.
|
||||
local -r line2="${line1//\x1B\[[0-9;]*[JKmsu]//g}" # This strips all codes - we test regexes against this.
|
||||
|
||||
if [[ $line2 =~ $excludeRegex ]]; then
|
||||
[[ -n $debugUs ]] && echo "[X] $line1"
|
||||
else
|
||||
[[ -n $debugUs ]] && echo " $line1" || echo "$line1"
|
||||
fi
|
||||
}
|
||||
|
||||
echoLine "Starting sbt with output filtering enabled."
|
||||
main | while read -r line; do echoLine "$line"; done
|
||||
}
|
||||
|
||||
# Only filter if there's a filter file and we don't see a known interactive command.
|
||||
# Obviously this is super ad hoc but I don't know how to improve on it. Testing whether
|
||||
# stdin is a terminal is useless because most of my use cases for this filtering are
|
||||
# exactly when I'm at a terminal, running sbt non-interactively.
|
||||
shouldFilter () { [[ -f ~/.sbtignore ]] && ! grep -E -q '\b(shell|console|consoleProject)\b' <<<"${residual_args[@]}"; }
|
||||
|
||||
# run sbt
|
||||
if shouldFilter; then mainFiltered; else main; fi
|
57
src/main/scala/CPU.scala
Normal file
57
src/main/scala/CPU.scala
Normal file
|
@ -0,0 +1,57 @@
|
|||
package FiveStage
|
||||
|
||||
import chisel3._
|
||||
import chisel3.core.Input
|
||||
import chisel3.experimental.MultiIOModule
|
||||
import chisel3.experimental._
|
||||
|
||||
|
||||
class CPU extends MultiIOModule {
|
||||
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val setupSignals = Input(new SetupSignals)
|
||||
val testReadouts = Output(new TestReadouts)
|
||||
val regUpdates = Output(new RegisterUpdates)
|
||||
val memUpdates = Output(new MemUpdates)
|
||||
val currentPC = Output(UInt(32.W))
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
You need to create the classes for these yourself
|
||||
*/
|
||||
// val IFBarrier = Module(new IFBarrier).io
|
||||
// val IDBarrier = Module(new IDBarrier).io
|
||||
// val EXBarrier = Module(new EXBarrier).io
|
||||
// val MEMBarrier = Module(new MEMBarrier).io
|
||||
|
||||
val ID = Module(new InstructionDecode)
|
||||
val IF = Module(new InstructionFetch)
|
||||
// val EX = Module(new Execute)
|
||||
val MEM = Module(new MemoryFetch)
|
||||
// val WB = Module(new Execute) (You may not need this one?)
|
||||
|
||||
|
||||
/**
|
||||
* Setup. You should not change this code
|
||||
*/
|
||||
IF.testHarness.IMEMsetup := testHarness.setupSignals.IMEMsignals
|
||||
ID.testHarness.registerSetup := testHarness.setupSignals.registerSignals
|
||||
MEM.testHarness.DMEMsetup := testHarness.setupSignals.DMEMsignals
|
||||
|
||||
testHarness.testReadouts.registerRead := ID.testHarness.registerPeek
|
||||
testHarness.testReadouts.DMEMread := MEM.testHarness.DMEMpeek
|
||||
|
||||
/**
|
||||
spying stuff
|
||||
*/
|
||||
testHarness.regUpdates := ID.testHarness.testUpdates
|
||||
testHarness.memUpdates := MEM.testHarness.testUpdates
|
||||
testHarness.currentPC := IF.testHarness.PC
|
||||
|
||||
|
||||
/**
|
||||
TODO: Your code here
|
||||
*/
|
||||
}
|
60
src/main/scala/Const.scala
Normal file
60
src/main/scala/Const.scala
Normal file
|
@ -0,0 +1,60 @@
|
|||
package FiveStage
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import chisel3.core.Input
|
||||
import chisel3.iotesters.PeekPokeTester
|
||||
|
||||
|
||||
// From RISC-V reference card
|
||||
object ALUOps {
|
||||
|
||||
val ADD = 0.U(4.W)
|
||||
val SUB = 1.U(4.W)
|
||||
val AND = 2.U(4.W)
|
||||
val OR = 3.U(4.W)
|
||||
val XOR = 4.U(4.W)
|
||||
val SLT = 5.U(4.W)
|
||||
val SLL = 6.U(4.W)
|
||||
val SLTU = 7.U(4.W)
|
||||
val SRL = 8.U(4.W)
|
||||
val SRA = 9.U(4.W)
|
||||
val COPY_A = 10.U(4.W)
|
||||
val COPY_B = 11.U(4.W)
|
||||
|
||||
val DC = 15.U(4.W)
|
||||
}
|
||||
|
||||
object lookup {
|
||||
def BEQ = BitPat("b?????????????????000?????1100011")
|
||||
def BNE = BitPat("b?????????????????001?????1100011")
|
||||
def BLT = BitPat("b?????????????????100?????1100011")
|
||||
def BGE = BitPat("b?????????????????101?????1100011")
|
||||
def BLTU = BitPat("b?????????????????110?????1100011")
|
||||
def BGEU = BitPat("b?????????????????111?????1100011")
|
||||
def JALR = BitPat("b?????????????????000?????1100111")
|
||||
def JAL = BitPat("b?????????????????????????1101111")
|
||||
def LUI = BitPat("b?????????????????????????0110111")
|
||||
def AUIPC = BitPat("b?????????????????????????0010111")
|
||||
def ADDI = BitPat("b?????????????????000?????0010011")
|
||||
def SLLI = BitPat("b000000???????????001?????0010011")
|
||||
def SLTI = BitPat("b?????????????????010?????0010011")
|
||||
def SLTIU = BitPat("b?????????????????011?????0010011")
|
||||
def XORI = BitPat("b?????????????????100?????0010011")
|
||||
def SRLI = BitPat("b000000???????????101?????0010011")
|
||||
def SRAI = BitPat("b010000???????????101?????0010011")
|
||||
def ORI = BitPat("b?????????????????110?????0010011")
|
||||
def ANDI = BitPat("b?????????????????111?????0010011")
|
||||
def ADD = BitPat("b0000000??????????000?????0110011")
|
||||
def SUB = BitPat("b0100000??????????000?????0110011")
|
||||
def SLL = BitPat("b0000000??????????001?????0110011")
|
||||
def SLT = BitPat("b0000000??????????010?????0110011")
|
||||
def SLTU = BitPat("b0000000??????????011?????0110011")
|
||||
def XOR = BitPat("b0000000??????????100?????0110011")
|
||||
def SRL = BitPat("b0000000??????????101?????0110011")
|
||||
def SRA = BitPat("b0100000??????????101?????0110011")
|
||||
def OR = BitPat("b0000000??????????110?????0110011")
|
||||
def AND = BitPat("b0000000??????????111?????0110011")
|
||||
def LW = BitPat("b?????????????????010?????0000011")
|
||||
def SW = BitPat("b?????????????????010?????0100011")
|
||||
}
|
54
src/main/scala/DMem.scala
Normal file
54
src/main/scala/DMem.scala
Normal file
|
@ -0,0 +1,54 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
|
||||
/**
|
||||
* This module is already done. Have one on me
|
||||
*/
|
||||
class DMEM extends MultiIOModule {
|
||||
|
||||
|
||||
// Don't touch the test harness
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val setup = Input(new DMEMsetupSignals)
|
||||
val testUpdates = Output(new MemUpdates)
|
||||
})
|
||||
|
||||
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
val writeEnable = Input(Bool())
|
||||
val dataIn = Input(UInt(32.W))
|
||||
val dataAddress = Input(UInt(12.W))
|
||||
|
||||
val dataOut = Output(UInt(32.W))
|
||||
})
|
||||
|
||||
val data = SyncReadMem(4096, UInt(32.W))
|
||||
|
||||
val addressSource = Wire(UInt(32.W))
|
||||
val dataSource = Wire(UInt(32.W))
|
||||
val writeEnableSource = Wire(Bool())
|
||||
|
||||
// For loading data
|
||||
when(testHarness.setup.setup){
|
||||
addressSource := testHarness.setup.dataAddress
|
||||
dataSource := testHarness.setup.dataIn
|
||||
writeEnableSource := testHarness.setup.writeEnable
|
||||
}.otherwise {
|
||||
addressSource := io.dataAddress
|
||||
dataSource := io.dataIn
|
||||
writeEnableSource := io.writeEnable
|
||||
}
|
||||
|
||||
testHarness.testUpdates.writeEnable := writeEnableSource
|
||||
testHarness.testUpdates.writeData := dataSource
|
||||
testHarness.testUpdates.writeAddress := addressSource
|
||||
|
||||
io.dataOut := data(addressSource)
|
||||
when(writeEnableSource){
|
||||
data(addressSource) := dataSource
|
||||
}
|
||||
}
|
82
src/main/scala/Decoder.scala
Normal file
82
src/main/scala/Decoder.scala
Normal file
|
@ -0,0 +1,82 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.util.BitPat
|
||||
import chisel3.util.ListLookup
|
||||
|
||||
|
||||
/**
|
||||
* This module is mostly done, but you will have to fill in the blanks in opcodeMap.
|
||||
* You may want to add more signals to be decoded in this module depending on your
|
||||
* design if you so desire.
|
||||
*
|
||||
* In the "classic" 5 stage decoder signals such as op1select and immType
|
||||
* are not included, however I have added them to my design, and similarily you might
|
||||
* find it useful to add more
|
||||
*/
|
||||
class Decoder() extends Module {
|
||||
|
||||
val io = IO(new Bundle {
|
||||
val instruction = Input(new Instruction)
|
||||
|
||||
val controlSignals = Output(new ControlSignals)
|
||||
val branchType = Output(UInt(3.W))
|
||||
val op1Select = Output(UInt(1.W))
|
||||
val op2Select = Output(UInt(1.W))
|
||||
val immType = Output(UInt(3.W))
|
||||
val ALUop = Output(UInt(4.W))
|
||||
})
|
||||
|
||||
import lookup._
|
||||
import Op1Select._
|
||||
import Op2Select._
|
||||
import branchType._
|
||||
import ImmFormat._
|
||||
|
||||
val N = 0.asUInt(1.W)
|
||||
val Y = 1.asUInt(1.W)
|
||||
|
||||
/**
|
||||
* In scala we sometimes (ab)use the `->` operator to create tuples.
|
||||
* The reason for this is that it serves as convenient sugar to make maps.
|
||||
*
|
||||
* This doesn't matter to you, just fill in the blanks in the style currently
|
||||
* used, I just want to demystify some of the magic.
|
||||
*
|
||||
* `a -> b` == `(a, b)` == `Tuple2(a, b)`
|
||||
*/
|
||||
val opcodeMap: Array[(BitPat, List[UInt])] = Array(
|
||||
|
||||
// signal memToReg, regWrite, memRead, memWrite, branch, jump, branchType, Op1Select, Op2Select, ImmSelect, ALUOp
|
||||
LW -> List(Y, Y, Y, N, N, N, branchType.DC, rs1, imm, ITYPE, ALUOps.ADD),
|
||||
|
||||
SW -> List(N, N, N, Y, N, N, branchType.DC, rs1, imm, STYPE, ALUOps.ADD),
|
||||
|
||||
ADD -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.ADD),
|
||||
SUB -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.SUB),
|
||||
|
||||
/**
|
||||
TODO: Fill in the blanks
|
||||
*/
|
||||
)
|
||||
|
||||
|
||||
val NOP = List(N, N, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.DC)
|
||||
|
||||
val decodedControlSignals = ListLookup(
|
||||
io.instruction.asUInt(),
|
||||
NOP,
|
||||
opcodeMap)
|
||||
|
||||
io.controlSignals.memToReg := decodedControlSignals(0)
|
||||
io.controlSignals.regWrite := decodedControlSignals(1)
|
||||
io.controlSignals.memRead := decodedControlSignals(2)
|
||||
io.controlSignals.memWrite := decodedControlSignals(3)
|
||||
io.controlSignals.branch := decodedControlSignals(4)
|
||||
io.controlSignals.jump := decodedControlSignals(5)
|
||||
|
||||
io.branchType := decodedControlSignals(6)
|
||||
io.op1Select := decodedControlSignals(7)
|
||||
io.op2Select := decodedControlSignals(8)
|
||||
io.immType := decodedControlSignals(9)
|
||||
io.ALUop := decodedControlSignals(10)
|
||||
}
|
49
src/main/scala/ID.scala
Normal file
49
src/main/scala/ID.scala
Normal file
|
@ -0,0 +1,49 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.util.{ BitPat, MuxCase }
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
|
||||
class InstructionDecode extends MultiIOModule {
|
||||
|
||||
// Don't touch the test harness
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val registerSetup = Input(new RegisterSetupSignals)
|
||||
val registerPeek = Output(UInt(32.W))
|
||||
|
||||
val testUpdates = Output(new RegisterUpdates)
|
||||
})
|
||||
|
||||
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
/**
|
||||
* TODO: Your code here.
|
||||
*/
|
||||
}
|
||||
)
|
||||
|
||||
val registers = Module(new Registers)
|
||||
val decoder = Module(new Decoder).io
|
||||
|
||||
|
||||
/**
|
||||
* Setup. You should not change this code
|
||||
*/
|
||||
registers.testHarness.setup := testHarness.registerSetup
|
||||
testHarness.registerPeek := registers.io.readData1
|
||||
testHarness.testUpdates := registers.testHarness.testUpdates
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Your code here.
|
||||
*/
|
||||
registers.io.readAddress1 := 0.U
|
||||
registers.io.readAddress2 := 0.U
|
||||
registers.io.writeEnable := false.B
|
||||
registers.io.writeAddress := 0.U
|
||||
registers.io.writeData := 0.U
|
||||
|
||||
decoder.instruction := 0.U.asTypeOf(new Instruction)
|
||||
}
|
59
src/main/scala/IF.scala
Normal file
59
src/main/scala/IF.scala
Normal file
|
@ -0,0 +1,59 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
class InstructionFetch extends MultiIOModule {
|
||||
|
||||
// Don't touch
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val IMEMsetup = Input(new IMEMsetupSignals)
|
||||
val PC = Output(UInt())
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Add signals for handling events such as jumps
|
||||
*/
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
val PC = Output(UInt())
|
||||
})
|
||||
|
||||
val IMEM = Module(new IMEM)
|
||||
val PC = RegInit(UInt(32.W), 0.U)
|
||||
|
||||
|
||||
/**
|
||||
* Setup. You should not change this code
|
||||
*/
|
||||
IMEM.testHarness.setupSignals := testHarness.IMEMsetup
|
||||
testHarness.PC := IMEM.testHarness.requestedAddress
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Your code here.
|
||||
*
|
||||
* You should expand on or rewrite the code below.
|
||||
*/
|
||||
io.PC := PC
|
||||
IMEM.io.instructionAddress := PC
|
||||
|
||||
PC := PC + 4.U
|
||||
|
||||
val instruction = Wire(new Instruction)
|
||||
instruction := IMEM.io.instruction.asTypeOf(new Instruction)
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Setup. You should not change this code
|
||||
*/
|
||||
when(testHarness.IMEMsetup.setup) {
|
||||
PC := 0.U
|
||||
// TODO: You must set the instruction to Instruction.NOP here.
|
||||
// throw new Exception("Just making sure you're seeing the line above")
|
||||
}
|
||||
}
|
52
src/main/scala/IMem.scala
Normal file
52
src/main/scala/IMem.scala
Normal file
|
@ -0,0 +1,52 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
/**
|
||||
* This module is finished and does not need to be modified to complete your fivestage.
|
||||
*
|
||||
* When setup is enabled data is written to the instruction memory.
|
||||
* In normal operation this memory is write only (no self modifying code)
|
||||
*/
|
||||
class IMEM() extends MultiIOModule {
|
||||
|
||||
// Don't touch
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val setupSignals = Input(new IMEMsetupSignals)
|
||||
val requestedAddress = Output(UInt())
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
val instructionAddress = Input(UInt(32.W))
|
||||
val instruction = Output(UInt(32.W))
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
SyncReadMem will output the value of the address signal set in the previous cycle.
|
||||
*/
|
||||
val instructions = SyncReadMem(4096, UInt(32.W))
|
||||
|
||||
// The address we want to read at during operation. During setup it acts as a write address
|
||||
// leading to the somewhat uninformative name shown here.
|
||||
val addressSource = Wire(UInt(32.W))
|
||||
|
||||
testHarness.requestedAddress := io.instructionAddress
|
||||
|
||||
when(testHarness.setupSignals.setup){
|
||||
addressSource := testHarness.setupSignals.address
|
||||
}.otherwise {
|
||||
addressSource := io.instructionAddress
|
||||
}
|
||||
|
||||
// For loading data
|
||||
when(testHarness.setupSignals.setup){
|
||||
instructions(addressSource) := testHarness.setupSignals.instruction
|
||||
}
|
||||
|
||||
io.instruction := instructions(addressSource)
|
||||
}
|
41
src/main/scala/MEM.scala
Normal file
41
src/main/scala/MEM.scala
Normal file
|
@ -0,0 +1,41 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
|
||||
class MemoryFetch() extends MultiIOModule {
|
||||
|
||||
|
||||
// Don't touch the test harness
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val DMEMsetup = Input(new DMEMsetupSignals)
|
||||
val DMEMpeek = Output(UInt(32.W))
|
||||
|
||||
val testUpdates = Output(new MemUpdates)
|
||||
})
|
||||
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
})
|
||||
|
||||
|
||||
val DMEM = Module(new DMEM)
|
||||
|
||||
|
||||
/**
|
||||
* Setup. You should not change this code
|
||||
*/
|
||||
DMEM.testHarness.setup := testHarness.DMEMsetup
|
||||
testHarness.DMEMpeek := DMEM.io.dataOut
|
||||
testHarness.testUpdates := DMEM.testHarness.testUpdates
|
||||
|
||||
|
||||
/**
|
||||
* Your code here.
|
||||
*/
|
||||
DMEM.io.dataIn := 0.U
|
||||
DMEM.io.dataAddress := 0.U
|
||||
DMEM.io.writeEnable := false.B
|
||||
}
|
81
src/main/scala/Registers.scala
Normal file
81
src/main/scala/Registers.scala
Normal file
|
@ -0,0 +1,81 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.experimental.MultiIOModule
|
||||
|
||||
|
||||
/**
|
||||
* This module is already done. Have one on me
|
||||
*
|
||||
* When a write and read conflicts this might result in stale data.
|
||||
* This caveat must be handled using a bypass.
|
||||
*/
|
||||
class Registers() extends MultiIOModule {
|
||||
|
||||
|
||||
// Don't touch the test harness
|
||||
val testHarness = IO(
|
||||
new Bundle {
|
||||
val setup = Input(new RegisterSetupSignals)
|
||||
val testUpdates = Output(new RegisterUpdates)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// You shouldn't really touch these either
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
val readAddress1 = Input(UInt(5.W))
|
||||
val readAddress2 = Input(UInt(5.W))
|
||||
val writeEnable = Input(Bool())
|
||||
val writeAddress = Input(UInt(5.W))
|
||||
val writeData = Input(UInt(32.W))
|
||||
|
||||
val readData1 = Output(UInt(32.W))
|
||||
val readData2 = Output(UInt(32.W))
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Mem creates asynchronous read, synchronous write.
|
||||
* In other words, reading is combinatory.
|
||||
*/
|
||||
val registerFile = Mem(32, UInt(32.W))
|
||||
|
||||
val readAddress1 = Wire(UInt(5.W))
|
||||
val readAddress2 = Wire(UInt(5.W))
|
||||
val writeAddress = Wire(UInt(5.W))
|
||||
val writeData = Wire(UInt(32.W))
|
||||
val writeEnable = Wire(Bool())
|
||||
|
||||
when(testHarness.setup.setup){
|
||||
readAddress1 := testHarness.setup.readAddress
|
||||
readAddress2 := io.readAddress2
|
||||
writeData := testHarness.setup.writeData
|
||||
writeEnable := testHarness.setup.writeEnable
|
||||
writeAddress := testHarness.setup.readAddress
|
||||
}.otherwise{
|
||||
readAddress1 := io.readAddress1
|
||||
readAddress2 := io.readAddress2
|
||||
writeData := io.writeData
|
||||
writeEnable := io.writeEnable
|
||||
writeAddress := io.writeAddress
|
||||
}
|
||||
|
||||
|
||||
testHarness.testUpdates.writeData := writeData
|
||||
testHarness.testUpdates.writeEnable := writeEnable
|
||||
testHarness.testUpdates.writeAddress := writeAddress
|
||||
|
||||
|
||||
when(writeEnable){
|
||||
when(writeAddress =/= 0.U){
|
||||
registerFile(writeAddress) := writeData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
io.readData1 := 0.U
|
||||
io.readData2 := 0.U
|
||||
when(readAddress1 =/= 0.U){ io.readData1 := registerFile(readAddress1) }
|
||||
when(readAddress2 =/= 0.U){ io.readData2 := registerFile(readAddress2) }
|
||||
}
|
51
src/main/scala/SetupSignals.scala
Normal file
51
src/main/scala/SetupSignals.scala
Normal file
|
@ -0,0 +1,51 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.core.Wire
|
||||
import chisel3.util.{ BitPat, Cat }
|
||||
|
||||
/**
|
||||
* Don't touch these
|
||||
*/
|
||||
class SetupSignals extends Bundle {
|
||||
val IMEMsignals = new IMEMsetupSignals
|
||||
val DMEMsignals = new DMEMsetupSignals
|
||||
val registerSignals = new RegisterSetupSignals
|
||||
}
|
||||
|
||||
class IMEMsetupSignals extends Bundle {
|
||||
val setup = Bool()
|
||||
val address = UInt(32.W)
|
||||
val instruction = UInt(32.W)
|
||||
}
|
||||
|
||||
class DMEMsetupSignals extends Bundle {
|
||||
val setup = Bool()
|
||||
val writeEnable = Bool()
|
||||
val dataIn = UInt(32.W)
|
||||
val dataAddress = UInt(32.W)
|
||||
}
|
||||
|
||||
class RegisterSetupSignals extends Bundle {
|
||||
val setup = Bool()
|
||||
val readAddress = UInt(5.W)
|
||||
val writeEnable = Bool()
|
||||
val writeAddress = UInt(5.W)
|
||||
val writeData = UInt(32.W)
|
||||
}
|
||||
|
||||
class TestReadouts extends Bundle {
|
||||
val registerRead = UInt(32.W)
|
||||
val DMEMread = UInt(32.W)
|
||||
}
|
||||
|
||||
class RegisterUpdates extends Bundle {
|
||||
val writeEnable = Bool()
|
||||
val writeData = UInt(32.W)
|
||||
val writeAddress = UInt(5.W)
|
||||
}
|
||||
|
||||
class MemUpdates extends Bundle {
|
||||
val writeEnable = Bool()
|
||||
val writeData = UInt(32.W)
|
||||
val writeAddress = UInt(32.W)
|
||||
}
|
75
src/main/scala/Tile.scala
Normal file
75
src/main/scala/Tile.scala
Normal file
|
@ -0,0 +1,75 @@
|
|||
package FiveStage
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import chisel3.core.Input
|
||||
import chisel3.iotesters.PeekPokeTester
|
||||
|
||||
|
||||
/**
|
||||
* The top level module. You do not have to change anything here,
|
||||
* however you are free to route out signals as you see fit for debugging.
|
||||
*/
|
||||
class Tile() extends Module{
|
||||
|
||||
val io = IO(
|
||||
new Bundle {
|
||||
val DMEMWriteData = Input(UInt(32.W))
|
||||
val DMEMAddress = Input(UInt(32.W))
|
||||
val DMEMWriteEnable = Input(Bool())
|
||||
val DMEMReadData = Output(UInt(32.W))
|
||||
|
||||
val regsWriteData = Input(UInt(32.W))
|
||||
val regsAddress = Input(UInt(5.W))
|
||||
val regsWriteEnable = Input(Bool())
|
||||
val regsReadData = Output(UInt(32.W))
|
||||
|
||||
val regsDeviceWriteEnable = Output(Bool())
|
||||
val regsDeviceWriteData = Output(UInt(32.W))
|
||||
val regsDeviceWriteAddress = Output(UInt(5.W))
|
||||
|
||||
val memDeviceWriteEnable = Output(Bool())
|
||||
val memDeviceWriteData = Output(UInt(32.W))
|
||||
val memDeviceWriteAddress = Output(UInt(32.W))
|
||||
|
||||
val IMEMWriteData = Input(UInt(32.W))
|
||||
val IMEMAddress = Input(UInt(32.W))
|
||||
|
||||
val setup = Input(Bool())
|
||||
|
||||
val currentPC = Output(UInt())
|
||||
})
|
||||
|
||||
val CPU = Module(new CPU).testHarness
|
||||
|
||||
CPU.setupSignals.IMEMsignals.address := io.IMEMAddress
|
||||
CPU.setupSignals.IMEMsignals.instruction := io.IMEMWriteData
|
||||
CPU.setupSignals.IMEMsignals.setup := io.setup
|
||||
|
||||
CPU.setupSignals.DMEMsignals.writeEnable := io.DMEMWriteEnable
|
||||
CPU.setupSignals.DMEMsignals.dataAddress := io.DMEMAddress
|
||||
CPU.setupSignals.DMEMsignals.dataIn := io.DMEMWriteData
|
||||
CPU.setupSignals.DMEMsignals.setup := io.setup
|
||||
|
||||
CPU.setupSignals.registerSignals.readAddress := io.regsAddress
|
||||
CPU.setupSignals.registerSignals.writeEnable := io.regsWriteEnable
|
||||
CPU.setupSignals.registerSignals.writeAddress := io.regsAddress
|
||||
CPU.setupSignals.registerSignals.writeData := io.regsWriteData
|
||||
CPU.setupSignals.registerSignals.setup := io.setup
|
||||
|
||||
io.DMEMReadData := CPU.testReadouts.DMEMread
|
||||
io.regsReadData := CPU.testReadouts.registerRead
|
||||
|
||||
io.regsDeviceWriteAddress := CPU.regUpdates.writeAddress
|
||||
io.regsDeviceWriteEnable := CPU.regUpdates.writeEnable
|
||||
io.regsDeviceWriteData := CPU.regUpdates.writeData
|
||||
|
||||
io.memDeviceWriteAddress := CPU.memUpdates.writeAddress
|
||||
io.memDeviceWriteEnable := CPU.memUpdates.writeEnable
|
||||
io.memDeviceWriteData := CPU.memUpdates.writeData
|
||||
|
||||
io.currentPC := CPU.currentPC
|
||||
}
|
||||
|
||||
|
||||
|
105
src/main/scala/ToplevelSignals.scala
Normal file
105
src/main/scala/ToplevelSignals.scala
Normal file
|
@ -0,0 +1,105 @@
|
|||
package FiveStage
|
||||
import chisel3._
|
||||
import chisel3.core.Wire
|
||||
import chisel3.util.{ BitPat, Cat }
|
||||
|
||||
|
||||
class Instruction extends Bundle(){
|
||||
val instruction = UInt(32.W)
|
||||
|
||||
def opcode = instruction(6, 0)
|
||||
def registerRd = instruction(11, 7)
|
||||
def funct3 = instruction(14, 12)
|
||||
def registerRs1 = instruction(19, 15)
|
||||
def registerRs2 = instruction(24, 20)
|
||||
def funct7 = instruction(31, 25)
|
||||
def funct6 = instruction(26, 31)
|
||||
|
||||
def immediateIType = instruction(31, 20).asSInt
|
||||
def immediateSType = Cat(instruction(31, 25), instruction(11,7)).asSInt
|
||||
def immediateBType = Cat(instruction(31), instruction(7), instruction(30, 25), instruction(11, 8), 0.U(1.W)).asSInt
|
||||
def immediateUType = Cat(instruction(31, 12), 0.U(12.W)).asSInt
|
||||
def immediateJType = Cat(instruction(31), instruction(19, 12), instruction(20), instruction(30, 25), instruction(24, 21), 0.U(1.W)).asSInt
|
||||
def immediateZType = instruction(19, 15).zext
|
||||
|
||||
}
|
||||
object Instruction {
|
||||
def bubble(i: Instruction) =
|
||||
i.opcode := BitPat.bitPatToUInt(BitPat("b0010011"))
|
||||
|
||||
def default: Instruction = {
|
||||
val w = Wire(new Instruction)
|
||||
w.instruction := 0.U
|
||||
w
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ControlSignals extends Bundle(){
|
||||
val memToReg = Bool()
|
||||
val regWrite = Bool()
|
||||
val memRead = Bool()
|
||||
val memWrite = Bool()
|
||||
val branch = Bool()
|
||||
val jump = Bool()
|
||||
}
|
||||
|
||||
|
||||
object ControlSignals {
|
||||
def nop: ControlSignals = {
|
||||
val b = Wire(new ControlSignals)
|
||||
b.memToReg := false.B
|
||||
b.regWrite := false.B
|
||||
b.memRead := false.B
|
||||
b.memWrite := false.B
|
||||
b.branch := false.B
|
||||
b.jump := false.B
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object branchType {
|
||||
val beq = 0.asUInt(3.W)
|
||||
val neq = 1.asUInt(3.W)
|
||||
val gte = 2.asUInt(3.W)
|
||||
val lt = 3.asUInt(3.W)
|
||||
val gteu = 4.asUInt(3.W)
|
||||
val ltu = 5.asUInt(3.W)
|
||||
val jump = 6.asUInt(3.W)
|
||||
val DC = 0.asUInt(3.W)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
these take the role of the alu source signal.
|
||||
Used in the decoder.
|
||||
In the solution manual I use these to select signals at the decode stage.
|
||||
You can choose to instead do this in the execute stage, and you may forego
|
||||
using them altogether.
|
||||
*/
|
||||
object Op1Select {
|
||||
val rs1 = 0.asUInt(1.W)
|
||||
val PC = 1.asUInt(1.W)
|
||||
val DC = 0.asUInt(1.W)
|
||||
}
|
||||
|
||||
object Op2Select {
|
||||
val rs2 = 0.asUInt(1.W)
|
||||
val imm = 1.asUInt(1.W)
|
||||
val DC = 0.asUInt(1.W)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Used in the decoder
|
||||
*/
|
||||
object ImmFormat {
|
||||
val ITYPE = 0.asUInt(3.W)
|
||||
val STYPE = 1.asUInt(3.W)
|
||||
val BTYPE = 2.asUInt(3.W)
|
||||
val UTYPE = 3.asUInt(3.W)
|
||||
val JTYPE = 4.asUInt(3.W)
|
||||
val SHAMT = 5.asUInt(3.W)
|
||||
val DC = 0.asUInt(3.W)
|
||||
}
|
52
src/test/resources/tests/basic/arithmetic/arith.s
Normal file
52
src/test/resources/tests/basic/arithmetic/arith.s
Normal file
|
@ -0,0 +1,52 @@
|
|||
main:
|
||||
addi x1, zero, 1
|
||||
addi x2, zero, -1
|
||||
addi x3, zero, 3
|
||||
addi x4, zero, 7
|
||||
addi x5, zero, 14
|
||||
addi x6, zero, 28
|
||||
addi x7, zero, 56
|
||||
addi x8, zero, 133
|
||||
addi x9, zero, 258
|
||||
addi x10, x1, -231
|
||||
addi x11, x1, -510
|
||||
slt x12, x1, x1
|
||||
slt x12, x1, x2
|
||||
slt x12, x1, x3
|
||||
slt x12, x2, x1
|
||||
slt x12, x2, x2
|
||||
slt x12, x2, x3
|
||||
sltu x12, x1, x1
|
||||
sltu x12, x1, x2
|
||||
sltu x12, x1, x3
|
||||
sltu x12, x2, x1
|
||||
sltu x12, x2, x2
|
||||
sltu x12, x2, x3
|
||||
sll x14, x1, x1
|
||||
sll x14, x1, x2
|
||||
sll x14, x1, x3
|
||||
srl x14, x1, x1
|
||||
srl x14, x1, x2
|
||||
srl x14, x1, x3
|
||||
sra x14, x1, x1
|
||||
sra x14, x1, x2
|
||||
sra x14, x1, x3
|
||||
and x15, x8, x9
|
||||
and x15, x9, x10
|
||||
or x15, x8, x9
|
||||
or x15, x9, x10
|
||||
xor x15, x8, x9
|
||||
xor x15, x9, x10
|
||||
add x16, x1, x5
|
||||
add x16, x2, x7
|
||||
add x16, x3, x6
|
||||
add x16, x4, x10
|
||||
add x16, x5, x14
|
||||
add x16, x6, x15
|
||||
add x16, x7, x7
|
||||
add x16, x8, x9
|
||||
add x16, x9, x1
|
||||
add x16, x10, x8
|
||||
add x16, x11, x9
|
||||
add x16, x12, x10
|
||||
done
|
205
src/test/resources/tests/basic/forward1.s
Normal file
205
src/test/resources/tests/basic/forward1.s
Normal file
|
@ -0,0 +1,205 @@
|
|||
main:
|
||||
ori gp, gp, 0xFFFFFE09
|
||||
and gp, gp, ra
|
||||
xor ra, ra, sp
|
||||
sra ra, sp, sp
|
||||
or sp, ra, ra
|
||||
xori ra, sp, 0x003D
|
||||
srli sp, sp, 0xFFFFFFF2
|
||||
addi ra, sp, 0x0012
|
||||
add ra, gp, sp
|
||||
ori gp, ra, 0xFFFFFFB8
|
||||
sll ra, ra, ra
|
||||
sll sp, gp, ra
|
||||
slt ra, gp, ra
|
||||
srai gp, gp, 0xFFFFFFFB
|
||||
slli ra, ra, 0x0007
|
||||
sub sp, gp, sp
|
||||
andi ra, gp, 0x002D
|
||||
sub gp, gp, sp
|
||||
srai ra, gp, 0x0005
|
||||
or sp, ra, sp
|
||||
xori ra, gp, 0xFFFFFF54
|
||||
sll ra, sp, ra
|
||||
sub gp, gp, sp
|
||||
add ra, sp, sp
|
||||
sll ra, gp, ra
|
||||
add gp, sp, sp
|
||||
ori gp, sp, 0xFFFFFE68
|
||||
srli sp, gp, 0xFFFFFFF1
|
||||
sltui ra, ra, 0xFFFFFE5D
|
||||
srli ra, gp, 0x0006
|
||||
srai sp, sp, 0x0005
|
||||
andi sp, sp, 0x0129
|
||||
and sp, sp, sp
|
||||
slli gp, ra, 0x0001
|
||||
or gp, gp, gp
|
||||
or sp, gp, gp
|
||||
slli gp, gp, 0x0006
|
||||
srl sp, ra, gp
|
||||
sltui gp, ra, 0xFFFFFEC3
|
||||
add sp, gp, sp
|
||||
xori ra, ra, 0xFFFFFFA2
|
||||
or gp, gp, sp
|
||||
and sp, gp, gp
|
||||
srai gp, sp, 0x0008
|
||||
add sp, sp, ra
|
||||
slti sp, ra, 0xFFFFFFF7
|
||||
srli gp, ra, 0xFFFFFFFD
|
||||
sll gp, ra, gp
|
||||
sltu sp, sp, gp
|
||||
srli gp, ra, 0x0002
|
||||
ori gp, sp, 0xFFFFFF28
|
||||
srl ra, gp, ra
|
||||
slti gp, ra, 0x0054
|
||||
or gp, ra, ra
|
||||
ori sp, gp, 0x01D9
|
||||
and sp, ra, ra
|
||||
addi sp, gp, 0x0054
|
||||
slli gp, ra, 0xFFFFFFF2
|
||||
ori sp, sp, 0x0093
|
||||
add gp, gp, gp
|
||||
and gp, gp, gp
|
||||
sltui gp, ra, 0x00DE
|
||||
slli sp, gp, 0x000D
|
||||
slli sp, ra, 0xFFFFFFF5
|
||||
sltui sp, ra, 0xFFFFFF0E
|
||||
and ra, gp, ra
|
||||
add gp, sp, sp
|
||||
slti ra, sp, 0x008C
|
||||
srli ra, sp, 0x0000
|
||||
addi sp, sp, 0x0168
|
||||
slli ra, ra, 0xFFFFFFF3
|
||||
addi ra, gp, 0x012A
|
||||
or sp, gp, ra
|
||||
add ra, sp, gp
|
||||
and gp, ra, ra
|
||||
slli ra, gp, 0xFFFFFFF6
|
||||
or sp, gp, sp
|
||||
or gp, ra, ra
|
||||
ori gp, ra, 0x00EB
|
||||
or sp, gp, ra
|
||||
ori gp, sp, 0x01DA
|
||||
andi ra, ra, 0xFFFFFFE9
|
||||
addi gp, sp, 0x00C9
|
||||
sltui ra, ra, 0xFFFFFF13
|
||||
sltui ra, ra, 0xFFFFFF3A
|
||||
sltui sp, gp, 0xFFFFFE5A
|
||||
ori sp, sp, 0xFFFFFFFE
|
||||
and gp, sp, gp
|
||||
sltui sp, ra, 0x0034
|
||||
srl gp, gp, ra
|
||||
sll gp, sp, ra
|
||||
ori ra, gp, 0xFFFFFEB6
|
||||
sll ra, sp, ra
|
||||
sra ra, gp, sp
|
||||
sub ra, sp, gp
|
||||
xor gp, gp, sp
|
||||
sub ra, ra, sp
|
||||
srl gp, gp, sp
|
||||
andi ra, ra, 0xFFFFFFCB
|
||||
ori ra, ra, 0xFFFFFE1B
|
||||
andi ra, ra, 0xFFFFFEC8
|
||||
sltui sp, gp, 0x0108
|
||||
sub sp, gp, ra
|
||||
slti ra, sp, 0x015D
|
||||
slli sp, sp, 0x0004
|
||||
xor gp, sp, ra
|
||||
srl ra, gp, ra
|
||||
sltui ra, ra, 0xFFFFFF3C
|
||||
add sp, sp, sp
|
||||
add gp, gp, ra
|
||||
andi sp, sp, 0xFFFFFF3A
|
||||
srli ra, sp, 0x0004
|
||||
ori sp, gp, 0xFFFFFEAB
|
||||
ori sp, ra, 0xFFFFFE95
|
||||
slli sp, sp, 0xFFFFFFF2
|
||||
xori gp, sp, 0x0040
|
||||
slti gp, sp, 0xFFFFFED1
|
||||
or sp, sp, sp
|
||||
sltui sp, gp, 0x01B4
|
||||
addi ra, gp, 0x002D
|
||||
and sp, gp, gp
|
||||
or ra, ra, ra
|
||||
or ra, gp, ra
|
||||
or ra, gp, ra
|
||||
sra ra, ra, gp
|
||||
sra gp, ra, sp
|
||||
sub ra, sp, ra
|
||||
srai ra, ra, 0x000F
|
||||
sltu sp, sp, ra
|
||||
slli ra, gp, 0xFFFFFFF5
|
||||
slti gp, gp, 0x00E0
|
||||
addi gp, ra, 0xFFFFFF72
|
||||
srl ra, ra, gp
|
||||
sltui gp, sp, 0xFFFFFEAA
|
||||
xor sp, ra, gp
|
||||
and gp, sp, ra
|
||||
srli gp, gp, 0x0003
|
||||
xori ra, ra, 0x01BD
|
||||
sub ra, gp, sp
|
||||
sll gp, ra, gp
|
||||
xori ra, sp, 0x0065
|
||||
or ra, sp, ra
|
||||
slt sp, gp, ra
|
||||
addi ra, sp, 0xFFFFFE34
|
||||
slli gp, sp, 0x0007
|
||||
sll ra, sp, gp
|
||||
sltui gp, gp, 0xFFFFFE62
|
||||
slti sp, sp, 0x0019
|
||||
xori ra, gp, 0x0092
|
||||
sltui gp, sp, 0xFFFFFF29
|
||||
srl sp, ra, gp
|
||||
xori sp, gp, 0xFFFFFF4C
|
||||
add sp, ra, gp
|
||||
add sp, gp, ra
|
||||
sra sp, sp, gp
|
||||
slli sp, ra, 0x0008
|
||||
srl sp, sp, sp
|
||||
add sp, gp, ra
|
||||
andi sp, sp, 0x0039
|
||||
sll ra, gp, sp
|
||||
andi gp, ra, 0xFFFFFECC
|
||||
sll sp, sp, sp
|
||||
sub sp, sp, ra
|
||||
srai ra, sp, 0x0008
|
||||
xor gp, ra, sp
|
||||
add sp, sp, sp
|
||||
sub gp, ra, gp
|
||||
xori gp, sp, 0x01EE
|
||||
and ra, ra, ra
|
||||
ori gp, ra, 0xFFFFFE96
|
||||
slli ra, gp, 0x0002
|
||||
srli gp, ra, 0x000D
|
||||
add ra, sp, sp
|
||||
andi sp, gp, 0xFFFFFEC0
|
||||
andi sp, gp, 0xFFFFFE7A
|
||||
xori ra, sp, 0x0169
|
||||
xori gp, sp, 0xFFFFFE02
|
||||
andi ra, ra, 0xFFFFFFD1
|
||||
xor ra, sp, gp
|
||||
xori gp, gp, 0x00AB
|
||||
srl ra, ra, gp
|
||||
and ra, ra, sp
|
||||
xori gp, sp, 0x005D
|
||||
srai sp, sp, 0x000A
|
||||
addi ra, sp, 0xFFFFFE19
|
||||
or sp, ra, ra
|
||||
addi ra, gp, 0x0084
|
||||
ori sp, sp, 0xFFFFFF3D
|
||||
xor gp, ra, gp
|
||||
sra ra, ra, gp
|
||||
xori ra, sp, 0x0040
|
||||
srai gp, gp, 0x0002
|
||||
xori ra, ra, 0xFFFFFE9A
|
||||
sra ra, sp, sp
|
||||
ori gp, sp, 0xFFFFFFB8
|
||||
sll sp, ra, ra
|
||||
sll sp, ra, gp
|
||||
sll gp, sp, sp
|
||||
sra gp, ra, ra
|
||||
srli gp, gp, 0x0001
|
||||
done
|
||||
#regset x1, 123
|
||||
#regset x2, -40
|
||||
#regset x3, 0xFFEE
|
205
src/test/resources/tests/basic/forward2.s
Normal file
205
src/test/resources/tests/basic/forward2.s
Normal file
|
@ -0,0 +1,205 @@
|
|||
main:
|
||||
sltui gp, ra, 0x01AE
|
||||
srli sp, sp, 0xFFFFFFFB
|
||||
addi ra, sp, 0x0177
|
||||
sub sp, ra, sp
|
||||
slli sp, ra, 0x000B
|
||||
add sp, gp, ra
|
||||
slli gp, sp, 0x0006
|
||||
ori sp, ra, 0xFFFFFF64
|
||||
and gp, gp, gp
|
||||
andi gp, gp, 0x0084
|
||||
xori ra, ra, 0xFFFFFEB4
|
||||
or sp, ra, ra
|
||||
addi sp, ra, 0x0078
|
||||
srli gp, sp, 0x0000
|
||||
srl sp, gp, sp
|
||||
andi ra, gp, 0xFFFFFFF4
|
||||
srai ra, ra, 0xFFFFFFFF
|
||||
sltu gp, sp, gp
|
||||
or sp, ra, gp
|
||||
sub ra, gp, ra
|
||||
addi sp, gp, 0x017C
|
||||
sltui ra, gp, 0xFFFFFF64
|
||||
xori sp, gp, 0x00A1
|
||||
xor ra, sp, gp
|
||||
ori gp, ra, 0x00B6
|
||||
add ra, ra, sp
|
||||
sltui gp, ra, 0xFFFFFFEC
|
||||
sltu gp, sp, ra
|
||||
sll sp, ra, gp
|
||||
add gp, ra, ra
|
||||
or gp, ra, gp
|
||||
xor sp, ra, sp
|
||||
addi sp, gp, 0xFFFFFF4C
|
||||
xor ra, sp, gp
|
||||
xori ra, sp, 0xFFFFFF72
|
||||
xori gp, sp, 0xFFFFFE95
|
||||
or ra, ra, ra
|
||||
slti ra, gp, 0xFFFFFE75
|
||||
slli sp, sp, 0xFFFFFFFC
|
||||
sltui ra, ra, 0xFFFFFE25
|
||||
add sp, ra, gp
|
||||
sltui gp, gp, 0xFFFFFFDB
|
||||
addi sp, sp, 0x003D
|
||||
sll ra, ra, sp
|
||||
ori ra, ra, 0x012C
|
||||
add gp, ra, gp
|
||||
xori sp, sp, 0x0157
|
||||
slti gp, sp, 0xFFFFFF2A
|
||||
and sp, ra, ra
|
||||
add gp, ra, ra
|
||||
sltui ra, gp, 0xFFFFFE56
|
||||
sra gp, gp, ra
|
||||
xori sp, gp, 0xFFFFFF0D
|
||||
sub sp, gp, ra
|
||||
slti ra, ra, 0x0154
|
||||
slli ra, ra, 0x000A
|
||||
ori ra, gp, 0xFFFFFEC2
|
||||
ori ra, sp, 0x0075
|
||||
addi gp, sp, 0x0079
|
||||
xor gp, ra, sp
|
||||
srli ra, sp, 0xFFFFFFF8
|
||||
slli gp, gp, 0x0006
|
||||
sra sp, gp, gp
|
||||
add sp, sp, sp
|
||||
slli gp, ra, 0xFFFFFFF0
|
||||
add sp, ra, sp
|
||||
srai ra, sp, 0x0005
|
||||
addi ra, gp, 0xFFFFFF83
|
||||
xor gp, sp, ra
|
||||
srli ra, ra, 0x0007
|
||||
sll gp, ra, gp
|
||||
xori gp, gp, 0x0163
|
||||
add ra, gp, gp
|
||||
add sp, ra, ra
|
||||
sltu ra, ra, sp
|
||||
sll sp, ra, ra
|
||||
ori sp, sp, 0xFFFFFF6B
|
||||
slli gp, gp, 0xFFFFFFFA
|
||||
xori sp, ra, 0x00A7
|
||||
add sp, ra, ra
|
||||
add ra, gp, gp
|
||||
addi gp, gp, 0xFFFFFFE9
|
||||
sra sp, ra, gp
|
||||
add gp, ra, ra
|
||||
ori gp, gp, 0x0002
|
||||
addi gp, gp, 0x002F
|
||||
sll sp, sp, ra
|
||||
srli sp, ra, 0xFFFFFFF2
|
||||
ori gp, sp, 0x00EB
|
||||
sra gp, ra, sp
|
||||
sra sp, sp, gp
|
||||
and gp, ra, ra
|
||||
sra ra, ra, gp
|
||||
add sp, gp, ra
|
||||
srl gp, sp, gp
|
||||
add ra, ra, sp
|
||||
srai gp, ra, 0xFFFFFFF2
|
||||
srli sp, ra, 0xFFFFFFFC
|
||||
ori gp, sp, 0xFFFFFE6E
|
||||
and gp, ra, ra
|
||||
ori gp, ra, 0xFFFFFFAF
|
||||
srl ra, ra, ra
|
||||
or sp, ra, ra
|
||||
ori gp, sp, 0x0018
|
||||
and gp, gp, gp
|
||||
slti gp, ra, 0x00C6
|
||||
sll gp, sp, gp
|
||||
srli gp, sp, 0xFFFFFFFA
|
||||
add gp, ra, ra
|
||||
add gp, ra, sp
|
||||
sra sp, ra, ra
|
||||
ori sp, ra, 0x0022
|
||||
and gp, gp, gp
|
||||
add ra, sp, ra
|
||||
sll sp, gp, gp
|
||||
ori gp, sp, 0x008E
|
||||
slti ra, gp, 0x00B5
|
||||
add sp, ra, sp
|
||||
and sp, gp, gp
|
||||
addi ra, sp, 0x0145
|
||||
and sp, gp, gp
|
||||
sll gp, ra, sp
|
||||
addi sp, sp, 0xFFFFFFAF
|
||||
xori sp, ra, 0xFFFFFE2C
|
||||
srl gp, ra, gp
|
||||
sub ra, sp, gp
|
||||
add sp, ra, ra
|
||||
slli sp, sp, 0x0006
|
||||
sub gp, ra, sp
|
||||
sltu sp, ra, gp
|
||||
xori ra, ra, 0xFFFFFF9A
|
||||
addi sp, sp, 0xFFFFFE12
|
||||
slli ra, ra, 0xFFFFFFFD
|
||||
add gp, gp, gp
|
||||
xori ra, ra, 0xFFFFFED7
|
||||
andi gp, gp, 0xFFFFFE05
|
||||
and gp, gp, sp
|
||||
addi gp, gp, 0xFFFFFEE5
|
||||
slli ra, gp, 0xFFFFFFF6
|
||||
sll sp, gp, gp
|
||||
and ra, gp, sp
|
||||
ori ra, gp, 0xFFFFFE22
|
||||
srl sp, sp, gp
|
||||
srli ra, sp, 0x0005
|
||||
slli ra, ra, 0xFFFFFFFB
|
||||
srai ra, ra, 0xFFFFFFF9
|
||||
srli ra, gp, 0xFFFFFFF1
|
||||
andi gp, gp, 0xFFFFFF96
|
||||
sra sp, gp, gp
|
||||
srai gp, gp, 0x000F
|
||||
sub sp, sp, gp
|
||||
sltui ra, ra, 0xFFFFFF41
|
||||
and sp, sp, sp
|
||||
xor gp, sp, ra
|
||||
srai gp, sp, 0xFFFFFFF6
|
||||
xori gp, gp, 0x00D3
|
||||
or gp, sp, sp
|
||||
sltu gp, ra, gp
|
||||
slli ra, gp, 0x0002
|
||||
or sp, gp, ra
|
||||
addi ra, ra, 0x002B
|
||||
addi gp, ra, 0x0035
|
||||
slli sp, gp, 0x0008
|
||||
addi gp, sp, 0x015E
|
||||
xor ra, gp, sp
|
||||
or ra, gp, ra
|
||||
sll ra, gp, ra
|
||||
sll gp, gp, ra
|
||||
srli gp, ra, 0xFFFFFFF4
|
||||
slt sp, ra, sp
|
||||
sltui sp, sp, 0xFFFFFE1C
|
||||
ori sp, ra, 0xFFFFFE83
|
||||
andi sp, sp, 0xFFFFFEFC
|
||||
addi ra, ra, 0xFFFFFF85
|
||||
ori gp, ra, 0x0084
|
||||
sll gp, gp, ra
|
||||
xori gp, sp, 0xFFFFFF6D
|
||||
sll gp, sp, gp
|
||||
sra ra, sp, ra
|
||||
xor ra, gp, sp
|
||||
srl ra, ra, sp
|
||||
srl ra, ra, sp
|
||||
andi gp, ra, 0xFFFFFE7B
|
||||
srai ra, sp, 0xFFFFFFF1
|
||||
sub sp, sp, ra
|
||||
or sp, gp, gp
|
||||
slt ra, ra, gp
|
||||
or gp, gp, sp
|
||||
srli ra, sp, 0xFFFFFFF5
|
||||
andi ra, gp, 0xFFFFFFD4
|
||||
sra sp, sp, sp
|
||||
add sp, ra, sp
|
||||
sub gp, ra, sp
|
||||
xori ra, gp, 0x0131
|
||||
add sp, sp, ra
|
||||
addi sp, gp, 0x0003
|
||||
sll ra, ra, ra
|
||||
slli gp, ra, 0x000E
|
||||
andi ra, gp, 0xFFFFFE88
|
||||
srai ra, gp, 0xFFFFFFFA
|
||||
done
|
||||
#regset x1, 123
|
||||
#regset x2, -40
|
||||
#regset x3, 0xFFEE
|
41
src/test/resources/tests/basic/immediate/addi.s
Normal file
41
src/test/resources/tests/basic/immediate/addi.s
Normal file
|
@ -0,0 +1,41 @@
|
|||
main:
|
||||
addi x0, x1, 1
|
||||
addi x0, x1, 2
|
||||
addi x0, x1, 3
|
||||
addi x0, x1, 7
|
||||
addi x0, x1, 14
|
||||
addi x0, x1, 28
|
||||
addi x0, x1, 56
|
||||
addi x31, x1, 1
|
||||
addi x31, x1, 2
|
||||
addi x31, x1, 3
|
||||
addi x31, x1, 7
|
||||
addi x31, x1, 14
|
||||
addi x31, x1, 28
|
||||
addi x31, x1, 56
|
||||
addi x31, x1, 133
|
||||
addi x31, x1, 258
|
||||
addi x31, x1, 511
|
||||
addi x31, x1, -1
|
||||
addi x31, x1, -3
|
||||
addi x31, x1, -9
|
||||
addi x31, x1, -98
|
||||
addi x31, x1, -231
|
||||
addi x31, x1, -510
|
||||
addi x30, x0, 1
|
||||
addi x30, x30, 2
|
||||
addi x30, x30, 3
|
||||
addi x30, x30, 7
|
||||
addi x30, x30, 14
|
||||
addi x30, x30, 28
|
||||
addi x30, x30, 56
|
||||
addi x30, x30, 133
|
||||
addi x30, x30, 258
|
||||
addi x30, x30, 511
|
||||
addi x30, x30, -1
|
||||
addi x30, x30, -3
|
||||
addi x30, x30, -9
|
||||
addi x30, x30, -98
|
||||
addi x30, x30, -231
|
||||
addi x30, x30, -510
|
||||
done
|
37
src/test/resources/tests/basic/immediate/arithImm.s
Normal file
37
src/test/resources/tests/basic/immediate/arithImm.s
Normal file
|
@ -0,0 +1,37 @@
|
|||
main:
|
||||
addi x1, zero, 1
|
||||
addi x2, zero, -1
|
||||
addi x3, zero, 3
|
||||
addi x4, zero, 7
|
||||
addi x5, zero, 14
|
||||
addi x6, zero, 28
|
||||
addi x7, zero, 56
|
||||
addi x8, zero, 133
|
||||
addi x9, zero, 258
|
||||
addi x10, x1, -231
|
||||
addi x11, x1, -510
|
||||
slti x12, x1, 1
|
||||
slti x12, x1, 10
|
||||
slti x12, x1, -10
|
||||
sltiu x13, x1, 1
|
||||
sltiu x13, x1, 10
|
||||
sltiu x13, x1, -10
|
||||
slti x12, x2, 1
|
||||
slti x12, x2, 10
|
||||
slti x12, x2, -10
|
||||
sltiu x13, x2, 1
|
||||
sltiu x13, x2, 10
|
||||
sltiu x13, x2, -10
|
||||
slli x14, x1, 16
|
||||
slli x14, x2, 16
|
||||
srai x14, x1, 16
|
||||
srai x14, x2, 16
|
||||
srli x14, x1, 16
|
||||
srli x14, x2, 16
|
||||
andi x15, x8, 3
|
||||
andi x15, x9, -1
|
||||
ori x15, x8, 3
|
||||
ori x15, x9, -1
|
||||
xori x15, x8, 3
|
||||
xori x15, x9, -1
|
||||
done
|
0
src/test/resources/tests/basic/immediate/arithmetic.s
Normal file
0
src/test/resources/tests/basic/immediate/arithmetic.s
Normal file
35
src/test/resources/tests/basic/load.s
Normal file
35
src/test/resources/tests/basic/load.s
Normal file
|
@ -0,0 +1,35 @@
|
|||
main:
|
||||
addi x1, zero, 4
|
||||
addi x2, zero, 4
|
||||
addi x3, zero, 4
|
||||
addi x4, zero, 4
|
||||
lw x3, 0(x3)
|
||||
nop
|
||||
nop
|
||||
lw x3, 0(x3)
|
||||
nop
|
||||
nop
|
||||
lw x3, 0(x3)
|
||||
nop
|
||||
nop
|
||||
lw x3, 0(x3)
|
||||
nop
|
||||
nop
|
||||
lw x2, 0(x2)
|
||||
nop
|
||||
lw x2, 0(x2)
|
||||
nop
|
||||
lw x2, 0(x2)
|
||||
nop
|
||||
lw x2, 0(x2)
|
||||
nop
|
||||
lw x1, 0(x1)
|
||||
lw x1, 0(x1)
|
||||
lw x1, 0(x1)
|
||||
lw x1, 0(x1)
|
||||
done
|
||||
#memset 0x0, 4
|
||||
#memset 0x4, 8
|
||||
#memset 0x8, 12
|
||||
#memset 0xc, 16
|
||||
#memset 0x10, 20
|
20
src/test/resources/tests/basic/load2.s
Normal file
20
src/test/resources/tests/basic/load2.s
Normal file
|
@ -0,0 +1,20 @@
|
|||
main:
|
||||
addi x1, zero, 4
|
||||
addi x2, zero, 4
|
||||
addi x3, zero, 4
|
||||
addi x4, zero, 4
|
||||
lw x1, 0(x1)
|
||||
add x1, x1, x1
|
||||
lw x1, 0(x1)
|
||||
sw x1, 4(x1)
|
||||
lw x1, 4(x1)
|
||||
done
|
||||
#memset 0x0, 4
|
||||
#memset 0x4, 8
|
||||
#memset 0x8, 12
|
||||
#memset 0xc, 16
|
||||
#memset 0x10, 20
|
||||
#memset 0x14, 20
|
||||
#memset 0x18, 20
|
||||
#memset 0x1c, 20
|
||||
#memset 0x20, 20
|
180
src/test/resources/tests/programs/BTreeManyO3.s
Normal file
180
src/test/resources/tests/programs/BTreeManyO3.s
Normal file
|
@ -0,0 +1,180 @@
|
|||
main:
|
||||
addi sp,sp,-16
|
||||
sw ra,12(sp)
|
||||
sw s0,8(sp)
|
||||
sw s1,4(sp)
|
||||
sw s2,0(sp)
|
||||
lw s2,0(zero)
|
||||
lw a5,0(s2)
|
||||
blez a5,.L17
|
||||
addi s0,s2,4
|
||||
slli a5,a5,2
|
||||
add s2,a5,s0
|
||||
li s1,0
|
||||
.L16:
|
||||
addi s0,s0,4
|
||||
lw a0,0(s0)
|
||||
call find
|
||||
add s1,s1,a0
|
||||
bne s0,s2,.L16
|
||||
.L14:
|
||||
mv a0,s1
|
||||
lw ra,12(sp)
|
||||
lw s0,8(sp)
|
||||
lw s1,4(sp)
|
||||
lw s2,0(sp)
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
.L17:
|
||||
li s1,0
|
||||
j .L14
|
||||
find:
|
||||
li a5,4
|
||||
j .L2
|
||||
.L12:
|
||||
mv a0,a5
|
||||
ret
|
||||
.L13:
|
||||
lw a5,0(a5)
|
||||
slli a5,a5,1
|
||||
andi a5,a5,508
|
||||
addi a5,a5,4
|
||||
j .L2
|
||||
.L4:
|
||||
bge a4,a0,.L9
|
||||
lw a4,0(a5)
|
||||
andi a4,a4,256
|
||||
beqz a4,.L10
|
||||
lw a5,0(a5)
|
||||
srli a5,a5,7
|
||||
andi a5,a5,508
|
||||
addi a5,a5,4
|
||||
.L2:
|
||||
lh a4,2(a5)
|
||||
beq a4,a0,.L12
|
||||
ble a4,a0,.L4
|
||||
lw a4,0(a5)
|
||||
andi a4,a4,1
|
||||
bnez a4,.L13
|
||||
li a0,-1
|
||||
ret
|
||||
.L9:
|
||||
li a0,-1
|
||||
ret
|
||||
.L10:
|
||||
li a0,-1
|
||||
ret
|
||||
#memset 0x0, 0x019C
|
||||
#memset 0x0004, 0x02D49D03
|
||||
#memset 0x0008, 0x00912305
|
||||
#memset 0x000C, 0x00301307
|
||||
#memset 0x0010, 0x001B0D09
|
||||
#memset 0x0014, 0x00010B00
|
||||
#memset 0x0018, 0x00090000
|
||||
#memset 0x001C, 0x001E110F
|
||||
#memset 0x0020, 0x001D0000
|
||||
#memset 0x0024, 0x001E0000
|
||||
#memset 0x0028, 0x00782115
|
||||
#memset 0x002C, 0x00661D17
|
||||
#memset 0x0030, 0x003C1B19
|
||||
#memset 0x0034, 0x00300000
|
||||
#memset 0x0038, 0x00430000
|
||||
#memset 0x003C, 0x00701F00
|
||||
#memset 0x0040, 0x00700000
|
||||
#memset 0x0044, 0x007B0000
|
||||
#memset 0x0048, 0x018E5925
|
||||
#memset 0x004C, 0x00A12B27
|
||||
#memset 0x0050, 0x009F0029
|
||||
#memset 0x0054, 0x00910000
|
||||
#memset 0x0058, 0x011D392D
|
||||
#memset 0x005C, 0x00E22F00
|
||||
#memset 0x0060, 0x01190031
|
||||
#memset 0x0064, 0x01090033
|
||||
#memset 0x0068, 0x00E93500
|
||||
#memset 0x006C, 0x00FF3700
|
||||
#memset 0x0070, 0x00FF0000
|
||||
#memset 0x0074, 0x01213F3B
|
||||
#memset 0x0078, 0x0120003D
|
||||
#memset 0x007C, 0x011F0000
|
||||
#memset 0x0080, 0x01835741
|
||||
#memset 0x0084, 0x016A5543
|
||||
#memset 0x0088, 0x012F4745
|
||||
#memset 0x008C, 0x012E0000
|
||||
#memset 0x0090, 0x014B4F49
|
||||
#memset 0x0094, 0x013A4B00
|
||||
#memset 0x0098, 0x01424D00
|
||||
#memset 0x009C, 0x01460000
|
||||
#memset 0x00A0, 0x015C0051
|
||||
#memset 0x00A4, 0x014D5300
|
||||
#memset 0x00A8, 0x01510000
|
||||
#memset 0x00AC, 0x01730000
|
||||
#memset 0x00B0, 0x018B0000
|
||||
#memset 0x00B4, 0x01BF655B
|
||||
#memset 0x00B8, 0x01A2615D
|
||||
#memset 0x00BC, 0x01995F00
|
||||
#memset 0x00C0, 0x019F0000
|
||||
#memset 0x00C4, 0x01A46300
|
||||
#memset 0x00C8, 0x01BD0000
|
||||
#memset 0x00CC, 0x027C8967
|
||||
#memset 0x00D0, 0x026C8569
|
||||
#memset 0x00D4, 0x01CC6D6B
|
||||
#memset 0x00D8, 0x01C60000
|
||||
#memset 0x00DC, 0x021F7D6F
|
||||
#memset 0x00E0, 0x02070071
|
||||
#memset 0x00E4, 0x01D37300
|
||||
#memset 0x00E8, 0x01F67B75
|
||||
#memset 0x00EC, 0x01EF7977
|
||||
#memset 0x00F0, 0x01D70000
|
||||
#memset 0x00F4, 0x01F10000
|
||||
#memset 0x00F8, 0x02060000
|
||||
#memset 0x00FC, 0x0254837F
|
||||
#memset 0x0100, 0x023D0081
|
||||
#memset 0x0104, 0x02200000
|
||||
#memset 0x0108, 0x02640000
|
||||
#memset 0x010C, 0x026D8700
|
||||
#memset 0x0110, 0x026E0000
|
||||
#memset 0x0114, 0x02C0008B
|
||||
#memset 0x0118, 0x0297998D
|
||||
#memset 0x011C, 0x0289978F
|
||||
#memset 0x0120, 0x02839591
|
||||
#memset 0x0124, 0x027C9300
|
||||
#memset 0x0128, 0x027E0000
|
||||
#memset 0x012C, 0x02850000
|
||||
#memset 0x0130, 0x028D0000
|
||||
#memset 0x0134, 0x029E009B
|
||||
#memset 0x0138, 0x02990000
|
||||
#memset 0x013C, 0x03D4C99F
|
||||
#memset 0x0140, 0x0332ADA1
|
||||
#memset 0x0144, 0x02E9A300
|
||||
#memset 0x0148, 0x031AABA5
|
||||
#memset 0x014C, 0x0305A9A7
|
||||
#memset 0x0150, 0x02EC0000
|
||||
#memset 0x0154, 0x03090000
|
||||
#memset 0x0158, 0x032B0000
|
||||
#memset 0x015C, 0x03CC00AF
|
||||
#memset 0x0160, 0x0365B7B1
|
||||
#memset 0x0164, 0x0333B300
|
||||
#memset 0x0168, 0x036200B5
|
||||
#memset 0x016C, 0x033E0000
|
||||
#memset 0x0170, 0x03B6C3B9
|
||||
#memset 0x0174, 0x03AA00BB
|
||||
#memset 0x0178, 0x039700BD
|
||||
#memset 0x017C, 0x037DC1BF
|
||||
#memset 0x0180, 0x03750000
|
||||
#memset 0x0184, 0x03870000
|
||||
#memset 0x0188, 0x03C8C7C5
|
||||
#memset 0x018C, 0x03BB0000
|
||||
#memset 0x0190, 0x03C90000
|
||||
#memset 0x0194, 0x03E0CB00
|
||||
#memset 0x0198, 0x03E40000
|
||||
#memset 0x019C, 0x0010
|
||||
#memset 0x01A0, 0x0264
|
||||
#memset 0x01A4, 0x0C0D
|
||||
#memset 0x01A8, 0x0031
|
||||
#memset 0x01AC, 0x0206
|
||||
#memset 0x01B0, 0x0891
|
||||
#memset 0x01B4, 0x0043
|
||||
#memset 0x01B8, 0x029E
|
||||
#memset 0x01BC, 0x0264
|
||||
#memset 0x01C0, 0x0123
|
||||
#memset 0x01C4, 0x0264
|
142
src/test/resources/tests/programs/BTreeO3.s
Normal file
142
src/test/resources/tests/programs/BTreeO3.s
Normal file
|
@ -0,0 +1,142 @@
|
|||
main:
|
||||
addi sp,sp,-16
|
||||
sw ra,12(sp)
|
||||
lw a0,0(zero)
|
||||
call find
|
||||
lw ra,12(sp)
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
find:
|
||||
li a5,4
|
||||
.L2:
|
||||
lh a4,2(a5)
|
||||
beq a4,a0,.L13
|
||||
.L11:
|
||||
ble a4,a0,.L4
|
||||
lw a5,0(a5)
|
||||
andi a4,a5,1
|
||||
bnez a4,.L14
|
||||
.L10:
|
||||
li a0,-1
|
||||
ret
|
||||
.L4:
|
||||
bge a4,a0,.L10
|
||||
lw a4,0(a5)
|
||||
srli a5,a4,7
|
||||
andi a4,a4,256
|
||||
andi a5,a5,508
|
||||
beqz a4,.L10
|
||||
addi a5,a5,4
|
||||
lh a4,2(a5)
|
||||
bne a4,a0,.L11
|
||||
.L13:
|
||||
mv a0,a5
|
||||
ret
|
||||
.L14:
|
||||
slli a5,a5,1
|
||||
andi a5,a5,508
|
||||
addi a5,a5,4
|
||||
j .L2
|
||||
#memset 0x0, 0x013A
|
||||
#memset 0x0004, 0x02D49D03
|
||||
#memset 0x0008, 0x00912305
|
||||
#memset 0x000C, 0x00301307
|
||||
#memset 0x0010, 0x001B0D09
|
||||
#memset 0x0014, 0x00010B00
|
||||
#memset 0x0018, 0x00090000
|
||||
#memset 0x001C, 0x001E110F
|
||||
#memset 0x0020, 0x001D0000
|
||||
#memset 0x0024, 0x001E0000
|
||||
#memset 0x0028, 0x00782115
|
||||
#memset 0x002C, 0x00661D17
|
||||
#memset 0x0030, 0x003C1B19
|
||||
#memset 0x0034, 0x00300000
|
||||
#memset 0x0038, 0x00430000
|
||||
#memset 0x003C, 0x00701F00
|
||||
#memset 0x0040, 0x00700000
|
||||
#memset 0x0044, 0x007B0000
|
||||
#memset 0x0048, 0x018E5925
|
||||
#memset 0x004C, 0x00A12B27
|
||||
#memset 0x0050, 0x009F0029
|
||||
#memset 0x0054, 0x00910000
|
||||
#memset 0x0058, 0x011D392D
|
||||
#memset 0x005C, 0x00E22F00
|
||||
#memset 0x0060, 0x01190031
|
||||
#memset 0x0064, 0x01090033
|
||||
#memset 0x0068, 0x00E93500
|
||||
#memset 0x006C, 0x00FF3700
|
||||
#memset 0x0070, 0x00FF0000
|
||||
#memset 0x0074, 0x01213F3B
|
||||
#memset 0x0078, 0x0120003D
|
||||
#memset 0x007C, 0x011F0000
|
||||
#memset 0x0080, 0x01835741
|
||||
#memset 0x0084, 0x016A5543
|
||||
#memset 0x0088, 0x012F4745
|
||||
#memset 0x008C, 0x012E0000
|
||||
#memset 0x0090, 0x014B4F49
|
||||
#memset 0x0094, 0x013A4B00
|
||||
#memset 0x0098, 0x01424D00
|
||||
#memset 0x009C, 0x01460000
|
||||
#memset 0x00A0, 0x015C0051
|
||||
#memset 0x00A4, 0x014D5300
|
||||
#memset 0x00A8, 0x01510000
|
||||
#memset 0x00AC, 0x01730000
|
||||
#memset 0x00B0, 0x018B0000
|
||||
#memset 0x00B4, 0x01BF655B
|
||||
#memset 0x00B8, 0x01A2615D
|
||||
#memset 0x00BC, 0x01995F00
|
||||
#memset 0x00C0, 0x019F0000
|
||||
#memset 0x00C4, 0x01A46300
|
||||
#memset 0x00C8, 0x01BD0000
|
||||
#memset 0x00CC, 0x027C8967
|
||||
#memset 0x00D0, 0x026C8569
|
||||
#memset 0x00D4, 0x01CC6D6B
|
||||
#memset 0x00D8, 0x01C60000
|
||||
#memset 0x00DC, 0x021F7D6F
|
||||
#memset 0x00E0, 0x02070071
|
||||
#memset 0x00E4, 0x01D37300
|
||||
#memset 0x00E8, 0x01F67B75
|
||||
#memset 0x00EC, 0x01EF7977
|
||||
#memset 0x00F0, 0x01D70000
|
||||
#memset 0x00F4, 0x01F10000
|
||||
#memset 0x00F8, 0x02060000
|
||||
#memset 0x00FC, 0x0254837F
|
||||
#memset 0x0100, 0x023D0081
|
||||
#memset 0x0104, 0x02200000
|
||||
#memset 0x0108, 0x02640000
|
||||
#memset 0x010C, 0x026D8700
|
||||
#memset 0x0110, 0x026E0000
|
||||
#memset 0x0114, 0x02C0008B
|
||||
#memset 0x0118, 0x0297998D
|
||||
#memset 0x011C, 0x0289978F
|
||||
#memset 0x0120, 0x02839591
|
||||
#memset 0x0124, 0x027C9300
|
||||
#memset 0x0128, 0x027E0000
|
||||
#memset 0x012C, 0x02850000
|
||||
#memset 0x0130, 0x028D0000
|
||||
#memset 0x0134, 0x029E009B
|
||||
#memset 0x0138, 0x02990000
|
||||
#memset 0x013C, 0x03D4C99F
|
||||
#memset 0x0140, 0x0332ADA1
|
||||
#memset 0x0144, 0x02E9A300
|
||||
#memset 0x0148, 0x031AABA5
|
||||
#memset 0x014C, 0x0305A9A7
|
||||
#memset 0x0150, 0x02EC0000
|
||||
#memset 0x0154, 0x03090000
|
||||
#memset 0x0158, 0x032B0000
|
||||
#memset 0x015C, 0x03CC00AF
|
||||
#memset 0x0160, 0x0365B7B1
|
||||
#memset 0x0164, 0x0333B300
|
||||
#memset 0x0168, 0x036200B5
|
||||
#memset 0x016C, 0x033E0000
|
||||
#memset 0x0170, 0x03B6C3B9
|
||||
#memset 0x0174, 0x03AA00BB
|
||||
#memset 0x0178, 0x039700BD
|
||||
#memset 0x017C, 0x037DC1BF
|
||||
#memset 0x0180, 0x03750000
|
||||
#memset 0x0184, 0x03870000
|
||||
#memset 0x0188, 0x03C8C7C5
|
||||
#memset 0x018C, 0x03BB0000
|
||||
#memset 0x0190, 0x03C90000
|
||||
#memset 0x0194, 0x03E0CB00
|
||||
#memset 0x0198, 0x03E40000
|
14
src/test/resources/tests/programs/add.s
Normal file
14
src/test/resources/tests/programs/add.s
Normal file
|
@ -0,0 +1,14 @@
|
|||
main:
|
||||
add t0, t0, t1
|
||||
add t2, t0, t1
|
||||
add zero, t0, t1
|
||||
add t2, t2, t1
|
||||
add t3, t3, t3
|
||||
add t3, t3, t3
|
||||
add t3, t3, t3
|
||||
nop
|
||||
done
|
||||
#regset t0,10
|
||||
#regset t1,23
|
||||
#regset t2,43
|
||||
#regset t3,-11
|
118
src/test/resources/tests/programs/memoFib.s
Normal file
118
src/test/resources/tests/programs/memoFib.s
Normal file
|
@ -0,0 +1,118 @@
|
|||
main:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
addi s0,sp,32
|
||||
sw zero,-20(s0)
|
||||
li a5,100
|
||||
sw a5,-24(s0)
|
||||
lw a1,-20(s0)
|
||||
li a0,11
|
||||
call setupmem
|
||||
lw a1,-24(s0)
|
||||
li a0,11
|
||||
call setupmem
|
||||
lw a2,-24(s0)
|
||||
lw a1,-20(s0)
|
||||
li a0,10
|
||||
call f
|
||||
li a5,0
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
||||
f:
|
||||
addi sp,sp,-48
|
||||
sw ra,44(sp)
|
||||
sw s0,40(sp)
|
||||
sw s1,36(sp)
|
||||
addi s0,sp,48
|
||||
sw a0,-36(s0)
|
||||
sw a1,-40(s0)
|
||||
sw a2,-44(s0)
|
||||
lw a5,-36(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-40(s0)
|
||||
add a5,a4,a5
|
||||
lw a5,0(a5)
|
||||
beqz a5,.L2
|
||||
lw a5,-36(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-44(s0)
|
||||
add a5,a4,a5
|
||||
lw a5,0(a5)
|
||||
j .L3
|
||||
.L2:
|
||||
lw a5,-36(s0)
|
||||
bnez a5,.L4
|
||||
li a5,0
|
||||
j .L3
|
||||
.L4:
|
||||
lw a4,-36(s0)
|
||||
li a5,1
|
||||
bne a4,a5,.L5
|
||||
li a5,1
|
||||
j .L3
|
||||
.L5:
|
||||
lw a5,-36(s0)
|
||||
addi a5,a5,-1
|
||||
lw a2,-44(s0)
|
||||
lw a1,-40(s0)
|
||||
mv a0,a5
|
||||
call f
|
||||
mv s1,a0
|
||||
lw a5,-36(s0)
|
||||
addi a5,a5,-2
|
||||
lw a2,-44(s0)
|
||||
lw a1,-40(s0)
|
||||
mv a0,a5
|
||||
call f
|
||||
mv a5,a0
|
||||
add a5,s1,a5
|
||||
sw a5,-20(s0)
|
||||
lw a5,-36(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-40(s0)
|
||||
add a5,a4,a5
|
||||
li a4,1
|
||||
sw a4,0(a5)
|
||||
lw a5,-36(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-44(s0)
|
||||
add a5,a4,a5
|
||||
lw a4,-20(s0)
|
||||
sw a4,0(a5)
|
||||
lw a5,-20(s0)
|
||||
.L3:
|
||||
mv a0,a5
|
||||
lw ra,44(sp)
|
||||
lw s0,40(sp)
|
||||
lw s1,36(sp)
|
||||
addi sp,sp,48
|
||||
jr ra
|
||||
setupmem:
|
||||
addi sp,sp,-48
|
||||
sw s0,44(sp)
|
||||
addi s0,sp,48
|
||||
sw a0,-36(s0)
|
||||
sw a1,-40(s0)
|
||||
sw zero,-20(s0)
|
||||
j .L7
|
||||
.L8:
|
||||
lw a5,-20(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-40(s0)
|
||||
add a5,a4,a5
|
||||
sw zero,0(a5)
|
||||
lw a5,-20(s0)
|
||||
addi a5,a5,1
|
||||
sw a5,-20(s0)
|
||||
.L7:
|
||||
lw a4,-20(s0)
|
||||
lw a5,-36(s0)
|
||||
blt a4,a5,.L8
|
||||
nop
|
||||
lw s0,44(sp)
|
||||
addi sp,sp,48
|
||||
jr ra
|
49
src/test/resources/tests/programs/naiveFib.s
Normal file
49
src/test/resources/tests/programs/naiveFib.s
Normal file
|
@ -0,0 +1,49 @@
|
|||
main:
|
||||
addi sp,sp,-16
|
||||
sw ra,12(sp)
|
||||
sw s0,8(sp)
|
||||
addi s0,sp,16
|
||||
li a0,6
|
||||
call f
|
||||
mv a5,a0
|
||||
mv a0,a5
|
||||
lw ra,12(sp)
|
||||
lw s0,8(sp)
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
f:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
sw s1,20(sp)
|
||||
addi s0,sp,32
|
||||
sw a0,-20(s0)
|
||||
lw a5,-20(s0)
|
||||
bnez a5,.L2
|
||||
li a5,0
|
||||
j .L3
|
||||
.L2:
|
||||
lw a4,-20(s0)
|
||||
li a5,1
|
||||
bne a4,a5,.L4
|
||||
li a5,1
|
||||
j .L3
|
||||
.L4:
|
||||
lw a5,-20(s0)
|
||||
addi a5,a5,-1
|
||||
mv a0,a5
|
||||
call f
|
||||
mv s1,a0
|
||||
lw a5,-20(s0)
|
||||
addi a5,a5,-2
|
||||
mv a0,a5
|
||||
call f
|
||||
mv a5,a0
|
||||
add a5,s1,a5
|
||||
.L3:
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
lw s1,20(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
112
src/test/resources/tests/programs/palindrome.s
Normal file
112
src/test/resources/tests/programs/palindrome.s
Normal file
|
@ -0,0 +1,112 @@
|
|||
main:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
addi s0,sp,32
|
||||
sw zero,-20(s0)
|
||||
li a5,32
|
||||
sw a5,-24(s0)
|
||||
li a2,7
|
||||
li a1,0
|
||||
lw a0,-20(s0)
|
||||
call isPalindrome
|
||||
.DEBUG_call1_return:
|
||||
mv a5,a0
|
||||
beqz a5,.L2
|
||||
li a2,15
|
||||
li a1,0
|
||||
lw a0,-24(s0)
|
||||
call isPalindrome
|
||||
.DEBUG_call2_return:
|
||||
mv a5,a0
|
||||
beqz a5,.L2
|
||||
li a5,1
|
||||
j .L4
|
||||
.L2:
|
||||
li a5,0
|
||||
.L4:
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
||||
isPalindrome:
|
||||
addi sp,sp,-48
|
||||
sw ra,44(sp)
|
||||
sw s0,40(sp)
|
||||
addi s0,sp,48
|
||||
sw a0,-36(s0)
|
||||
sw a1,-40(s0)
|
||||
sw a2,-44(s0)
|
||||
lw a4,-40(s0)
|
||||
lw a5,-44(s0)
|
||||
blt a4,a5,.L6
|
||||
li a5,1
|
||||
j .L7
|
||||
.L6:
|
||||
lw a5,-40(s0)
|
||||
slli a5,a5,2
|
||||
lw a4,-36(s0)
|
||||
add a5,a4,a5
|
||||
lw a4,0(a5)
|
||||
lw a5,-44(s0)
|
||||
slli a5,a5,2
|
||||
lw a3,-36(s0)
|
||||
add a5,a3,a5
|
||||
lw a5,0(a5)
|
||||
sub a5,a4,a5
|
||||
seqz a5,a5
|
||||
andi a5,a5,0xff
|
||||
sw a5,-20(s0)
|
||||
lw a5,-20(s0)
|
||||
beqz a5,.CompareFailed
|
||||
lw a5,-40(s0)
|
||||
addi a4,a5,1
|
||||
lw a5,-44(s0)
|
||||
addi a5,a5,-1
|
||||
mv a2,a5
|
||||
mv a1,a4
|
||||
lw a0,-36(s0)
|
||||
call isPalindrome
|
||||
mv a5,a0
|
||||
beqz a5,.CompareFailed
|
||||
li a5,1
|
||||
j .L7
|
||||
.CompareFailed:
|
||||
li a5,0
|
||||
.L7:
|
||||
mv a0,a5
|
||||
lw ra,44(sp)
|
||||
lw s0,40(sp)
|
||||
addi sp,sp,48
|
||||
jr ra
|
||||
|
||||
#memset 0, 10
|
||||
#memset 4, -3
|
||||
#memset 8, 8
|
||||
#memset 12, 0
|
||||
#memset 16, 0
|
||||
#memset 20, 8
|
||||
#memset 24, -3
|
||||
#memset 28, 10
|
||||
|
||||
|
||||
#memset 32, 10
|
||||
#memset 36, -3
|
||||
#memset 40, 8
|
||||
#memset 44, 0
|
||||
#memset 48, 0
|
||||
#memset 52, 10
|
||||
|
||||
#memset 56, -3
|
||||
#memset 60, 8
|
||||
|
||||
#memset 64, -3
|
||||
#memset 68, 8
|
||||
|
||||
#memset 72, 10
|
||||
#memset 76, 0
|
||||
#memset 80, 0
|
||||
#memset 84, 8
|
||||
#memset 88, -3
|
||||
#memset 92, 10
|
120
src/test/resources/tests/programs/palindromeO3.s
Normal file
120
src/test/resources/tests/programs/palindromeO3.s
Normal file
|
@ -0,0 +1,120 @@
|
|||
main:
|
||||
addi sp,sp,-16
|
||||
li a2,7
|
||||
li a1,0
|
||||
li a0,0
|
||||
sw ra,12(sp)
|
||||
call isPalindrome.part.0
|
||||
.DEBUG_call1_return:
|
||||
bnez a0,.L17
|
||||
isPalindrome.part.0:
|
||||
slli a4,a1,2
|
||||
slli a3,a2,2
|
||||
add a4,a0,a4
|
||||
add a5,a0,a3
|
||||
lw a4,0(a4)
|
||||
lw a5,0(a5)
|
||||
beq a4,a5,.L10
|
||||
li a5,0
|
||||
.L6:
|
||||
mv a0,a5
|
||||
ret
|
||||
.L10:
|
||||
addi a1,a1,1
|
||||
addi a2,a2,-1
|
||||
li a5,1
|
||||
bge a1,a2,.L6
|
||||
addi sp,sp,-16
|
||||
sw ra,12(sp)
|
||||
call isPalindrome.part.0
|
||||
.DEBUG_call2_return:
|
||||
lw ra,12(sp)
|
||||
snez a5,a0
|
||||
mv a0,a5
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
.L11:
|
||||
lw ra,12(sp)
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
.L17:
|
||||
li a2,15
|
||||
li a1,0
|
||||
li a0,32
|
||||
call isPalindrome.part.0
|
||||
snez a0,a0
|
||||
j .L11
|
||||
isPalindrome:
|
||||
bge a1,a2,.L20
|
||||
slli a3,a2,2
|
||||
slli a4,a1,2
|
||||
add a5,a0,a3
|
||||
add a4,a0,a4
|
||||
lw a7,0(a4)
|
||||
lw a6,0(a5)
|
||||
li a3,0
|
||||
bne a7,a6,.L28
|
||||
addi a7,a1,1
|
||||
addi a6,a2,-1
|
||||
li a3,1
|
||||
bge a7,a6,.L28
|
||||
lw a7,4(a4)
|
||||
lw a6,-4(a5)
|
||||
li a3,0
|
||||
bne a7,a6,.L28
|
||||
addi a7,a1,2
|
||||
addi a6,a2,-2
|
||||
li a3,1
|
||||
bge a7,a6,.L28
|
||||
lw a4,8(a4)
|
||||
lw a5,-8(a5)
|
||||
li a3,0
|
||||
bne a4,a5,.L28
|
||||
addi a1,a1,3
|
||||
addi a2,a2,-3
|
||||
li a3,1
|
||||
bge a1,a2,.L28
|
||||
addi sp,sp,-16
|
||||
sw ra,12(sp)
|
||||
call isPalindrome.part.0
|
||||
lw ra,12(sp)
|
||||
snez a3,a0
|
||||
mv a0,a3
|
||||
addi sp,sp,16
|
||||
jr ra
|
||||
.L20:
|
||||
li a3,1
|
||||
.L28:
|
||||
mv a0,a3
|
||||
ret
|
||||
|
||||
|
||||
#memset 0, 10
|
||||
#memset 4, -3
|
||||
#memset 8, 8
|
||||
#memset 12, 0
|
||||
#memset 16, 0
|
||||
#memset 20, 8
|
||||
#memset 24, -3
|
||||
#memset 28, 10
|
||||
|
||||
|
||||
#memset 32, 10
|
||||
#memset 36, -3
|
||||
#memset 40, 8
|
||||
#memset 44, 0
|
||||
#memset 48, 0
|
||||
#memset 52, 10
|
||||
|
||||
#memset 56, -3
|
||||
#memset 60, 8
|
||||
|
||||
#memset 64, -3
|
||||
#memset 68, 8
|
||||
|
||||
#memset 72, 10
|
||||
#memset 76, 0
|
||||
#memset 80, 0
|
||||
#memset 84, 8
|
||||
#memset 88, -3
|
||||
#memset 92, 10
|
187
src/test/resources/tests/programs/searchRegularO0.s
Normal file
187
src/test/resources/tests/programs/searchRegularO0.s
Normal file
|
@ -0,0 +1,187 @@
|
|||
main:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
addi s0,sp,32
|
||||
li a5,0
|
||||
lw a5,0(a5)
|
||||
sw a5,-20(s0)
|
||||
lw a0,-20(s0)
|
||||
call find
|
||||
mv a5,a0
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
||||
find:
|
||||
addi sp,sp,-64
|
||||
sw s0,60(sp)
|
||||
addi s0,sp,64
|
||||
sw a0,-52(s0)
|
||||
sw zero,-24(s0)
|
||||
li a5,4
|
||||
sw a5,-20(s0)
|
||||
sw zero,-28(s0)
|
||||
j .L2
|
||||
.WHILE:
|
||||
lw a5,-20(s0)
|
||||
lh a5,2(a5)
|
||||
sw a5,-28(s0)
|
||||
lw a4,-28(s0)
|
||||
lw a5,-52(s0)
|
||||
bne a4,a5,.CHECKLEFT
|
||||
lw a5,-20(s0)
|
||||
j .L1
|
||||
.CHECKLEFT:
|
||||
lw a4,-28(s0)
|
||||
lw a5,-52(s0)
|
||||
ble a4,a5,.CHECKRIGHT
|
||||
lw a5,-20(s0)
|
||||
lw a5,0(a5)
|
||||
andi a5,a5,1
|
||||
beqz a5,.CHECKRIGHT
|
||||
lw a5,-20(s0)
|
||||
lw a5,0(a5)
|
||||
srli a5,a5,1
|
||||
andi a5,a5,127
|
||||
andi a5,a5,0xff
|
||||
sw a5,-32(s0)
|
||||
lw a5,-32(s0)
|
||||
slli a5,a5,2
|
||||
addi a5,a5,4
|
||||
sw a5,-20(s0)
|
||||
j .L2
|
||||
.CHECKRIGHT:
|
||||
lw a4,-28(s0)
|
||||
lw a5,-52(s0)
|
||||
bge a4,a5,.L6
|
||||
lw a5,-20(s0)
|
||||
lw a5,0(a5)
|
||||
andi a5,a5,256
|
||||
beqz a5,.L6
|
||||
lw a5,-20(s0)
|
||||
lw a5,0(a5)
|
||||
srli a5,a5,9
|
||||
andi a5,a5,127
|
||||
andi a5,a5,0xff
|
||||
sw a5,-36(s0)
|
||||
lw a5,-36(s0)
|
||||
slli a5,a5,2
|
||||
addi a5,a5,4
|
||||
sw a5,-20(s0)
|
||||
j .L2
|
||||
.L6:
|
||||
li a5,-1
|
||||
j .L1
|
||||
.L2:
|
||||
lw a5,-24(s0)
|
||||
beqz a5,.WHILE
|
||||
.L1:
|
||||
mv a0,a5
|
||||
lw s0,60(sp)
|
||||
addi sp,sp,64
|
||||
jr ra
|
||||
#memset 0x0, 0x013A
|
||||
#memset 0x0004, 0x02D49D03
|
||||
#memset 0x0008, 0x00912305
|
||||
#memset 0x000C, 0x00301307
|
||||
#memset 0x0010, 0x001B0D09
|
||||
#memset 0x0014, 0x00010B00
|
||||
#memset 0x0018, 0x00090000
|
||||
#memset 0x001C, 0x001E110F
|
||||
#memset 0x0020, 0x001D0000
|
||||
#memset 0x0024, 0x001E0000
|
||||
#memset 0x0028, 0x00782115
|
||||
#memset 0x002C, 0x00661D17
|
||||
#memset 0x0030, 0x003C1B19
|
||||
#memset 0x0034, 0x00300000
|
||||
#memset 0x0038, 0x00430000
|
||||
#memset 0x003C, 0x00701F00
|
||||
#memset 0x0040, 0x00700000
|
||||
#memset 0x0044, 0x007B0000
|
||||
#memset 0x0048, 0x018E5925
|
||||
#memset 0x004C, 0x00A12B27
|
||||
#memset 0x0050, 0x009F0029
|
||||
#memset 0x0054, 0x00910000
|
||||
#memset 0x0058, 0x011D392D
|
||||
#memset 0x005C, 0x00E22F00
|
||||
#memset 0x0060, 0x01190031
|
||||
#memset 0x0064, 0x01090033
|
||||
#memset 0x0068, 0x00E93500
|
||||
#memset 0x006C, 0x00FF3700
|
||||
#memset 0x0070, 0x00FF0000
|
||||
#memset 0x0074, 0x01213F3B
|
||||
#memset 0x0078, 0x0120003D
|
||||
#memset 0x007C, 0x011F0000
|
||||
#memset 0x0080, 0x01835741
|
||||
#memset 0x0084, 0x016A5543
|
||||
#memset 0x0088, 0x012F4745
|
||||
#memset 0x008C, 0x012E0000
|
||||
#memset 0x0090, 0x014B4F49
|
||||
#memset 0x0094, 0x013A4B00
|
||||
#memset 0x0098, 0x01424D00
|
||||
#memset 0x009C, 0x01460000
|
||||
#memset 0x00A0, 0x015C0051
|
||||
#memset 0x00A4, 0x014D5300
|
||||
#memset 0x00A8, 0x01510000
|
||||
#memset 0x00AC, 0x01730000
|
||||
#memset 0x00B0, 0x018B0000
|
||||
#memset 0x00B4, 0x01BF655B
|
||||
#memset 0x00B8, 0x01A2615D
|
||||
#memset 0x00BC, 0x01995F00
|
||||
#memset 0x00C0, 0x019F0000
|
||||
#memset 0x00C4, 0x01A46300
|
||||
#memset 0x00C8, 0x01BD0000
|
||||
#memset 0x00CC, 0x027C8967
|
||||
#memset 0x00D0, 0x026C8569
|
||||
#memset 0x00D4, 0x01CC6D6B
|
||||
#memset 0x00D8, 0x01C60000
|
||||
#memset 0x00DC, 0x021F7D6F
|
||||
#memset 0x00E0, 0x02070071
|
||||
#memset 0x00E4, 0x01D37300
|
||||
#memset 0x00E8, 0x01F67B75
|
||||
#memset 0x00EC, 0x01EF7977
|
||||
#memset 0x00F0, 0x01D70000
|
||||
#memset 0x00F4, 0x01F10000
|
||||
#memset 0x00F8, 0x02060000
|
||||
#memset 0x00FC, 0x0254837F
|
||||
#memset 0x0100, 0x023D0081
|
||||
#memset 0x0104, 0x02200000
|
||||
#memset 0x0108, 0x02640000
|
||||
#memset 0x010C, 0x026D8700
|
||||
#memset 0x0110, 0x026E0000
|
||||
#memset 0x0114, 0x02C0008B
|
||||
#memset 0x0118, 0x0297998D
|
||||
#memset 0x011C, 0x0289978F
|
||||
#memset 0x0120, 0x02839591
|
||||
#memset 0x0124, 0x027C9300
|
||||
#memset 0x0128, 0x027E0000
|
||||
#memset 0x012C, 0x02850000
|
||||
#memset 0x0130, 0x028D0000
|
||||
#memset 0x0134, 0x029E009B
|
||||
#memset 0x0138, 0x02990000
|
||||
#memset 0x013C, 0x03D4C99F
|
||||
#memset 0x0140, 0x0332ADA1
|
||||
#memset 0x0144, 0x02E9A300
|
||||
#memset 0x0148, 0x031AABA5
|
||||
#memset 0x014C, 0x0305A9A7
|
||||
#memset 0x0150, 0x02EC0000
|
||||
#memset 0x0154, 0x03090000
|
||||
#memset 0x0158, 0x032B0000
|
||||
#memset 0x015C, 0x03CC00AF
|
||||
#memset 0x0160, 0x0365B7B1
|
||||
#memset 0x0164, 0x0333B300
|
||||
#memset 0x0168, 0x036200B5
|
||||
#memset 0x016C, 0x033E0000
|
||||
#memset 0x0170, 0x03B6C3B9
|
||||
#memset 0x0174, 0x03AA00BB
|
||||
#memset 0x0178, 0x039700BD
|
||||
#memset 0x017C, 0x037DC1BF
|
||||
#memset 0x0180, 0x03750000
|
||||
#memset 0x0184, 0x03870000
|
||||
#memset 0x0188, 0x03C8C7C5
|
||||
#memset 0x018C, 0x03BB0000
|
||||
#memset 0x0190, 0x03C90000
|
||||
#memset 0x0194, 0x03E0CB00
|
||||
#memset 0x0198, 0x03E40000
|
38
src/test/resources/tests/programs/source/memoFib.c
Normal file
38
src/test/resources/tests/programs/source/memoFib.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <stdio.h>
|
||||
|
||||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O3"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
|
||||
int f(int x, int* isMemoized, int* memoizedVal){
|
||||
if(isMemoized[x])
|
||||
return memoizedVal[x];
|
||||
|
||||
if (x == 0) return 0;
|
||||
if (x == 1) return 1;
|
||||
|
||||
int next = f(x-1, isMemoized, memoizedVal) + f(x-2, isMemoized, memoizedVal);
|
||||
isMemoized[x] = 1;
|
||||
memoizedVal[x] = next;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
void setupmem(int n, int* m) {
|
||||
for(int ii = 0; ii < n; ii++){
|
||||
m[ii] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int* isMemoized = (int*)0;
|
||||
int* memoizedVal = (int*)100;
|
||||
setupmem(11, isMemoized);
|
||||
setupmem(11, memoizedVal);
|
||||
int r = f(10, isMemoized, memoizedVal);
|
||||
return r;
|
||||
}
|
20
src/test/resources/tests/programs/source/naiveFib.c
Normal file
20
src/test/resources/tests/programs/source/naiveFib.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O0"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
int f(int x){
|
||||
if (x == 0) return 0;
|
||||
if (x == 1) return 1;
|
||||
return f(x-1) + f(x-2);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
return f(4);
|
||||
}
|
28
src/test/resources/tests/programs/source/palindrome.c
Normal file
28
src/test/resources/tests/programs/source/palindrome.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O3"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
int main() {
|
||||
|
||||
/* int palindrome[8]; */
|
||||
/* int notAPalindrome[16]; */
|
||||
|
||||
// Set up "heap" addresses
|
||||
int palindrome = (int*)0;
|
||||
int notAPalindrome = (int*)32;
|
||||
|
||||
return isPalindrome(palindrome, 0, 7) && isPalindrome(notAPalindrome, 0, 15);
|
||||
}
|
||||
|
||||
int isPalindrome(int* word, int start, int stop){
|
||||
if(start >= stop){
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
int currentIsPalindrome = (word[start] == word[stop]);
|
||||
return currentIsPalindrome && isPalindrome(word, start + 1, stop - 1);
|
||||
}
|
||||
}
|
50
src/test/resources/tests/programs/source/search.c
Normal file
50
src/test/resources/tests/programs/source/search.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O3"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
/**
|
||||
* Represents a binary tree
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int hasLeft : 1;
|
||||
unsigned int leftIndex : 7;
|
||||
unsigned int hasRight : 1;
|
||||
unsigned int rightIndex : 7;
|
||||
int value : 16;
|
||||
} node;
|
||||
|
||||
|
||||
int find(int findMe){
|
||||
int found = 0;
|
||||
node* currentNodeIdx = (node*)4;
|
||||
int currentValue = 0;
|
||||
|
||||
while(!found){
|
||||
currentValue = currentNodeIdx->value;
|
||||
if(currentValue == findMe){
|
||||
return (int)currentNodeIdx;
|
||||
}
|
||||
|
||||
if((currentValue > findMe) && currentNodeIdx->hasLeft){
|
||||
int nextNodeIdx = currentNodeIdx->leftIndex;
|
||||
currentNodeIdx = (node*)(4 + (nextNodeIdx << 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
if((currentValue < findMe) && currentNodeIdx->hasRight){
|
||||
int nextNodeIdx = currentNodeIdx->rightIndex;
|
||||
currentNodeIdx = (node*)(4 + (nextNodeIdx << 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int needle = *(int*)0;
|
||||
return find(needle);
|
||||
}
|
64
src/test/resources/tests/programs/source/searchMany.c
Normal file
64
src/test/resources/tests/programs/source/searchMany.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O1"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
/**
|
||||
* Represents a binary tree
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int hasLeft : 1;
|
||||
unsigned int leftIndex : 7;
|
||||
unsigned int hasRight : 1;
|
||||
unsigned int rightIndex : 7;
|
||||
int value : 16;
|
||||
} node;
|
||||
|
||||
|
||||
int find(int findMe){
|
||||
int found = 0;
|
||||
node* currentNodeIdx = (node*)4;
|
||||
int currentValue = 0;
|
||||
|
||||
while(!found){
|
||||
currentValue = currentNodeIdx->value;
|
||||
if(currentValue == findMe){
|
||||
return (int)currentNodeIdx;
|
||||
}
|
||||
|
||||
if((currentValue > findMe) && currentNodeIdx->hasLeft){
|
||||
int nextNodeIdx = currentNodeIdx->leftIndex;
|
||||
currentNodeIdx = (node*)(4 + (nextNodeIdx << 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
if((currentValue < findMe) && currentNodeIdx->hasRight){
|
||||
int nextNodeIdx = currentNodeIdx->rightIndex;
|
||||
currentNodeIdx = (node*)(4 + (nextNodeIdx << 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Where does the needle list start?
|
||||
int needles = *(int*)0;
|
||||
int sum = 0;
|
||||
|
||||
// How many needles are there?
|
||||
int numNeedles = *(int*)needles;
|
||||
int nextNeedle = (int)needles + 4;
|
||||
|
||||
// Some useless calculations to make gcc O3 happy
|
||||
for(int ii = 0; ii < numNeedles; ii++){
|
||||
nextNeedle += 4;
|
||||
int needle = *(int*)(nextNeedle);
|
||||
sum += find(needle);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
35
src/test/resources/tests/programs/source/square.c
Normal file
35
src/test/resources/tests/programs/source/square.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
// C rmsbolt starter file
|
||||
|
||||
// Local Variables:
|
||||
// rmsbolt-command: "/opt/riscv/bin/riscv32-unknown-elf-gcc -O0"
|
||||
// rmsbolt-disassemble: nil
|
||||
// End:
|
||||
|
||||
|
||||
int mul(int a, int b) {
|
||||
int c = 0;
|
||||
int ii = 0;
|
||||
for(int ii = 0; ii < a; ii++){
|
||||
c += b;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int square(int a){
|
||||
return mul(a, a);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int a = 6;
|
||||
int b = 0xFFFFFFFE; // MAXVAL - 2, (a + b) = -2
|
||||
int c = -1; //-1
|
||||
int d = 7; //0x4D2 (c + d) = 0x4D1
|
||||
|
||||
if(square(a+b) > square(c + d))
|
||||
return a;
|
||||
else
|
||||
return c;
|
||||
}
|
80
src/test/resources/tests/programs/square.s
Normal file
80
src/test/resources/tests/programs/square.s
Normal file
|
@ -0,0 +1,80 @@
|
|||
main:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
sw s1,20(sp)
|
||||
addi s0,sp,32
|
||||
li a5,6
|
||||
sw a5,-20(s0)
|
||||
li a5,-2
|
||||
sw a5,-24(s0)
|
||||
li a5,-1
|
||||
sw a5,-28(s0)
|
||||
li a5,7
|
||||
sw a5,-32(s0)
|
||||
lw a4,-20(s0)
|
||||
lw a5,-24(s0)
|
||||
add a5,a4,a5
|
||||
mv a0,a5
|
||||
call square
|
||||
mv s1,a0
|
||||
lw a4,-28(s0)
|
||||
lw a5,-32(s0)
|
||||
add a5,a4,a5
|
||||
mv a0,a5
|
||||
call square
|
||||
mv a5,a0
|
||||
ble s1,a5,.L8
|
||||
lw a5,-20(s0)
|
||||
j .L9
|
||||
.L8:
|
||||
lw a5,-28(s0)
|
||||
.L9:
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
lw s1,20(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
||||
mul:
|
||||
addi sp,sp,-48
|
||||
sw s0,44(sp)
|
||||
addi s0,sp,48
|
||||
sw a0,-36(s0)
|
||||
sw a1,-40(s0)
|
||||
sw zero,-20(s0)
|
||||
sw zero,-28(s0)
|
||||
sw zero,-24(s0)
|
||||
j .L2
|
||||
.L3:
|
||||
lw a4,-20(s0)
|
||||
lw a5,-40(s0)
|
||||
add a5,a4,a5
|
||||
sw a5,-20(s0)
|
||||
lw a5,-24(s0)
|
||||
addi a5,a5,1
|
||||
sw a5,-24(s0)
|
||||
.L2:
|
||||
lw a4,-24(s0)
|
||||
lw a5,-36(s0)
|
||||
blt a4,a5,.L3
|
||||
lw a5,-20(s0)
|
||||
mv a0,a5
|
||||
lw s0,44(sp)
|
||||
addi sp,sp,48
|
||||
jr ra
|
||||
square:
|
||||
addi sp,sp,-32
|
||||
sw ra,28(sp)
|
||||
sw s0,24(sp)
|
||||
addi s0,sp,32
|
||||
sw a0,-20(s0)
|
||||
lw a1,-20(s0)
|
||||
lw a0,-20(s0)
|
||||
call mul
|
||||
mv a5,a0
|
||||
mv a0,a5
|
||||
lw ra,28(sp)
|
||||
lw s0,24(sp)
|
||||
addi sp,sp,32
|
||||
jr ra
|
78
src/test/scala/Manifest.scala
Normal file
78
src/test/scala/Manifest.scala
Normal file
|
@ -0,0 +1,78 @@
|
|||
package FiveStage
|
||||
import org.scalatest.{Matchers, FlatSpec}
|
||||
import cats._
|
||||
import cats.implicits._
|
||||
import fileUtils._
|
||||
|
||||
import chisel3.iotesters._
|
||||
import scala.collection.mutable.LinkedHashMap
|
||||
|
||||
import fansi.Str
|
||||
|
||||
import Ops._
|
||||
import Data._
|
||||
import VM._
|
||||
|
||||
import PrintUtils._
|
||||
import LogParser._
|
||||
|
||||
object Manifest {
|
||||
|
||||
val singleTest = "forward2.s"
|
||||
|
||||
val nopPadded = false
|
||||
|
||||
val singleTestOptions = TestOptions(
|
||||
printIfSuccessful = true,
|
||||
printErrors = true,
|
||||
printParsedProgram = false,
|
||||
printVMtrace = false,
|
||||
printVMfinal = false,
|
||||
printMergedTrace = true,
|
||||
nopPadded = nopPadded,
|
||||
breakPoints = Nil, // not implemented
|
||||
testName = singleTest)
|
||||
|
||||
|
||||
val allTestOptions: String => TestOptions = name => TestOptions(
|
||||
printIfSuccessful = false,
|
||||
printErrors = false,
|
||||
printParsedProgram = false,
|
||||
printVMtrace = false,
|
||||
printVMfinal = false,
|
||||
printMergedTrace = false,
|
||||
nopPadded = nopPadded,
|
||||
breakPoints = Nil, // not implemented
|
||||
testName = name)
|
||||
|
||||
}
|
||||
|
||||
|
||||
class SingleTest extends FlatSpec with Matchers {
|
||||
it should "just werk" in {
|
||||
TestRunner.run(Manifest.singleTestOptions) should be(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AllTests extends FlatSpec with Matchers {
|
||||
it should "just werk" in {
|
||||
val werks = getAllTestNames.map{testname =>
|
||||
say(s"testing $testname")
|
||||
val opts = Manifest.allTestOptions(testname)
|
||||
(testname, TestRunner.run(opts))
|
||||
}
|
||||
if(werks.foldLeft(true)(_ && _._2))
|
||||
say(Console.GREEN + "All tests successful!" + Console.RESET)
|
||||
else {
|
||||
val success = werks.map(x => if(x._2) 1 else 0).sum
|
||||
val total = werks.size
|
||||
say(s"$success/$total tests successful")
|
||||
werks.foreach{ case(name, success) =>
|
||||
val msg = if(success) Console.GREEN + s"$name successful" + Console.RESET
|
||||
else Console.RED + s"$name failed" + Console.RESET
|
||||
say(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
357
src/test/scala/RISCV/DataTypes.scala
Normal file
357
src/test/scala/RISCV/DataTypes.scala
Normal file
|
@ -0,0 +1,357 @@
|
|||
package FiveStage
|
||||
import cats.data.Writer
|
||||
import cats._
|
||||
import cats.data._
|
||||
import cats.implicits._
|
||||
|
||||
import fansi._
|
||||
import PrintUtils._
|
||||
|
||||
import fileUtils.say
|
||||
|
||||
/**
|
||||
* Types and extension methods go here.
|
||||
* Maybe it's a litte arbitrary to put VM types here, but op types in Ops.scala
|
||||
* Maybe these could be separated to somewhere else.
|
||||
*/
|
||||
object Data {
|
||||
type Label = String
|
||||
|
||||
case class Reg(value: Int)
|
||||
case class Imm(value: Int)
|
||||
case class Addr(value: Int){
|
||||
def +(that: Addr) = Addr(value + that.value)
|
||||
def -(that: Addr) = Addr(value - that.value)
|
||||
def step = Addr(value + 4)
|
||||
}
|
||||
|
||||
object Reg{ def apply(s: String): Reg = Reg(lookupReg(s).get) }
|
||||
|
||||
type SourceInfo[A] = Writer[List[String], A]
|
||||
object SourceInfo { def apply[A](s: String, a: A) = Writer(List(s), a) }
|
||||
|
||||
trait ExecutionEvent
|
||||
import PrintUtils._
|
||||
case class RegUpdate(reg: Reg, word: Int) extends ExecutionEvent
|
||||
case class MemWrite(addr: Addr, word: Int) extends ExecutionEvent
|
||||
case class MemRead(addr: Addr, word: Int) extends ExecutionEvent
|
||||
|
||||
// addr is the target address
|
||||
case class PcUpdateJALR(addr: Addr) extends ExecutionEvent
|
||||
case class PcUpdateJAL(addr: Addr) extends ExecutionEvent
|
||||
case class PcUpdateB(addr: Addr) extends ExecutionEvent
|
||||
case class PcUpdate(addr: Addr) extends ExecutionEvent
|
||||
|
||||
case class ExecutionTraceEvent(pc: Addr, event: ExecutionEvent*){ override def toString(): String = s"$pc: " + event.toList.mkString(", ") }
|
||||
type ExecutionTrace[A] = Writer[List[ExecutionTraceEvent], A]
|
||||
|
||||
object ExecutionTrace {
|
||||
def apply(vm: VM, event: ExecutionTraceEvent*) = Writer(event.toList, vm)
|
||||
}
|
||||
|
||||
|
||||
sealed trait ChiselEvent
|
||||
case class ChiselRegEvent(pcAddr: Addr, reg: Reg, word: Int) extends ChiselEvent
|
||||
case class ChiselMemWriteEvent(pcAddr: Addr, memAddr: Addr, word: Int) extends ChiselEvent
|
||||
|
||||
type CircuitTrace = (Addr, List[ChiselEvent])
|
||||
|
||||
|
||||
/**
|
||||
* Not sure these should be defined here instead of in the VM
|
||||
*/
|
||||
case class Regs(repr: Map[Reg, Int]) {
|
||||
def +(a: (Reg, Int)): (Option[RegUpdate], Regs) =
|
||||
if(a._1.value == 0) (None, this)
|
||||
else (Some(RegUpdate(a._1, a._2)), copy(repr + a))
|
||||
|
||||
def arith(rd: Reg, operand1: Reg, operand2: Reg, op: (Int, Int) => Int): (Option[RegUpdate], Regs) =
|
||||
this + (rd -> op(repr(operand1), repr(operand2)))
|
||||
|
||||
def arithImm(rd: Reg, operand1: Reg, operand2: Imm, op: (Int, Int) => Int): (Option[RegUpdate], Regs) =
|
||||
this + (rd -> op(repr(operand1), operand2.value))
|
||||
|
||||
def compare(operand1: Reg, operand2: Reg, comp: (Int, Int) => Boolean): Boolean =
|
||||
comp(repr(operand1), repr(operand2))
|
||||
|
||||
def apply(setting: TestSetting): Regs = setting match {
|
||||
case setting: REGSET => Regs(repr + (setting.rd -> setting.word))
|
||||
case _ => this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class DMem(repr: Map[Addr, Int]) {
|
||||
def read(addr: Addr): Either[String, (MemRead, Int)] =
|
||||
if(addr.value >= 4096)
|
||||
Left(s"attempted to read from illegal address ${addr.show}")
|
||||
else {
|
||||
val readResult = repr.lift(addr).getOrElse(0)
|
||||
Right((MemRead(addr, readResult), readResult))
|
||||
}
|
||||
|
||||
def write(addr: Addr, word: Int): Either[String, (MemWrite, DMem)] =
|
||||
if(addr.value >= 4096)
|
||||
Left(s"attempted to write to illegal address ${addr.show}")
|
||||
else {
|
||||
Right((MemWrite(addr, word)), DMem(repr + (addr -> word)))
|
||||
}
|
||||
|
||||
def apply(setting: TestSetting): DMem = setting match {
|
||||
case setting: MEMSET => {
|
||||
DMem(repr + (setting.addr -> setting.word))
|
||||
}
|
||||
case _ => this
|
||||
}
|
||||
}
|
||||
|
||||
object Regs{
|
||||
def empty: Regs = Regs((0 to 31).map(x => (Reg(x) -> 0)).toMap)
|
||||
def apply(settings: List[TestSetting]): Regs = settings.foldLeft(empty){
|
||||
case(acc, setting) => acc(setting)
|
||||
}
|
||||
}
|
||||
object DMem{
|
||||
def empty: DMem = DMem(Map[Addr, Int]())
|
||||
def apply(settings: List[TestSetting]): DMem = settings.foldLeft(empty){
|
||||
case(acc, setting) => acc(setting)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
implicit class IntOps(i: Int) {
|
||||
// Needs backticks to not conflict with xml
|
||||
def `u>`(that: Int): Boolean = {
|
||||
if((i >= 0) && (that >= 0))
|
||||
i > that
|
||||
else if((i < 0) && (that < 0))
|
||||
i > that
|
||||
else if((i < 0))
|
||||
true
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
def nBitsS: Int = i match {
|
||||
case i if (i < 0) => (math.log(math.abs(i.toLong))/math.log(2)).ceil.toInt + 1
|
||||
case i if (i == 0) => 0
|
||||
case i if (i > 0) => (math.log((i + 1).toLong)/math.log(2)).ceil.toInt + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Yes, a negative number technically has a unsigned size, but that depends on integer width,
|
||||
* so it is better left as an option
|
||||
*/
|
||||
def nBitsU: Option[Int] = i match {
|
||||
case i if (i < 0) => None
|
||||
case i if (i == 0) => Some(0)
|
||||
case i if (i > 0) => Some((math.log(i)/math.log(2)).ceil.toInt)
|
||||
}
|
||||
|
||||
def field(firstBit: Int, size: Int): Int = {
|
||||
val bitsLeft = 31 - firstBit
|
||||
val bitsRight = 32 - size
|
||||
val leftShifted = i << bitsLeft
|
||||
val rightShifted = leftShifted >> bitsRight
|
||||
rightShifted
|
||||
}
|
||||
|
||||
def splitLoHi(loBits: Int): (Int, Int) = {
|
||||
val hiBits = 32 - loBits
|
||||
val sep = 31 - loBits
|
||||
val lo = i.field(31, loBits)
|
||||
val hi = i.field(sep, hiBits)
|
||||
(lo, hi)
|
||||
}
|
||||
|
||||
def log2: Int = math.ceil(math.log(i.toDouble)/math.log(2.0)).toInt
|
||||
}
|
||||
|
||||
implicit class StringOps(s: String) {
|
||||
def binary: Int = {
|
||||
s.reverse.foldLeft((0, 0)){
|
||||
case((acc, pow), char) if char == '0' => (acc, pow + 1)
|
||||
case((acc, pow), char) if char == '1' => (acc + (1 << pow), pow + 1)
|
||||
case((acc, pow), char) => assert(false, "malformed binary conversion"); (0, 0)
|
||||
}._1
|
||||
}
|
||||
}
|
||||
|
||||
implicit class ListOps[A](xs: List[A]) {
|
||||
def mkStringN = xs.mkString("\n","\n","\n")
|
||||
def splitAtPred(p: (A, A) => Boolean): List[List[A]] = {
|
||||
val splitPoints = xs.tail.foldLeft((1, List[Int](), xs.head)){
|
||||
case((idx, acc, pA), a) if(p(pA, a)) => (1, idx :: acc, a)
|
||||
case((idx, acc, pA), a) => (idx + 1, acc, a)
|
||||
}._2.reverse
|
||||
|
||||
val (blocks, rem) = splitPoints.foldLeft((List[List[A]](), xs)){
|
||||
case((acc, rem), point) => {
|
||||
val(block, remainder) = rem.splitAt(point)
|
||||
(block :: acc, remainder)
|
||||
}
|
||||
}
|
||||
(rem :: blocks).reverse
|
||||
}
|
||||
def showN(sep: String)(implicit ev: Fancy[A]): fansi.Str =
|
||||
xs.foldLeft(fansi.Str("")){ case(acc, a) => acc ++ a.show ++ fansi.Str(sep) }
|
||||
def showN(sep1: String, sep2: String, sep3: String)(implicit ev: Fancy[A]): fansi.Str =
|
||||
Str(sep1) ++ xs.foldLeft(fansi.Str("")){ case(acc, a) => acc ++ a.show ++ fansi.Str(sep2) } ++ Str(sep3)
|
||||
def shuffle(shuffler: scala.util.Random): List[A] = shuffler.shuffle(xs)
|
||||
}
|
||||
implicit class NestedListOps[A](xs: List[List[A]]) {
|
||||
def zipWithIndexNested: List[List[(A, Int)]] = {
|
||||
val startingPoints = xs.scanLeft(0){ case(acc, xs) => acc + xs.size }
|
||||
(xs.map(_.zipWithIndex) zip startingPoints).map{ case(withIndex, offset) =>
|
||||
withIndex.map{ case(a, idx) => (a, idx + offset) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Ops._
|
||||
|
||||
sealed trait TestSetting
|
||||
case class REGSET(rd: Reg, word: Int) extends TestSetting
|
||||
case class MEMSET(addr: Addr, word: Int) extends TestSetting
|
||||
|
||||
implicit class ListEitherOps[E,A](es: List[Either[E,A]]) {
|
||||
import cats.data.Validated
|
||||
def separateXOR: Either[List[E], List[A]] = {
|
||||
val (errors, as) = es.map(_.toValidated).separate
|
||||
Either.cond(errors.isEmpty, as, errors)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents the result of parsing a program, with built in convenience methods for running a VM
|
||||
* and assembling the program.
|
||||
*/
|
||||
case class Program(
|
||||
ops : List[SourceInfo[Op]],
|
||||
settings : List[TestSetting],
|
||||
labelMap : Map[Label, Addr],
|
||||
maxSteps : Int = 5000
|
||||
){
|
||||
|
||||
def imem: Map[Addr, Op] =
|
||||
ops.map(_.run._2).zipWithIndex.map{ case(op, idx) => (Addr(idx*4), op) }.toMap
|
||||
|
||||
|
||||
/**
|
||||
* Loads a VM which can be run to get a trace.
|
||||
*/
|
||||
def vm: VM =
|
||||
VM(settings, imem, labelMap)
|
||||
|
||||
|
||||
/**
|
||||
* A convenient lookup for every instruction, allowing the test runner to check what source line
|
||||
* caused an error to happen.
|
||||
*/
|
||||
val sourceMap: Map[Addr, String] =
|
||||
ops.map(_.run._1).zipWithIndex.map{ case(info, idx) => (Addr(idx*4), info.map(_.trim).mkString(", ")) }.toMap
|
||||
|
||||
|
||||
/**
|
||||
* The assembled program
|
||||
*/
|
||||
def machineCode: Either[String, Map[Addr, Int]] =
|
||||
imem.toList
|
||||
.sortBy(_._1.value).map{ case(addr, op) => assembler.assembleOp(op, addr, labelMap).map(x => (addr, x)) }
|
||||
.sequence
|
||||
.map(_.toMap)
|
||||
.left.map{ case(error, addr) => s"Assembler error: $error, corresponding to source:\n${sourceMap(addr)}" }
|
||||
|
||||
|
||||
/**
|
||||
* Returns the binary code and the execution trace or an error for convenient error checking.
|
||||
*/
|
||||
def validate: Either[String, (Map[Addr, Int], ExecutionTrace[VM])] = machineCode.flatMap{ binary =>
|
||||
val uk = "UNKNOWN"
|
||||
val (finish, trace) = VM.run(maxSteps, vm)
|
||||
finish match {
|
||||
case Failed(s, addr) => Left(s"VM failed with error $s at address $addr\nSource line:\n${sourceMap.lift(addr).getOrElse(uk)}")
|
||||
case Timeout => Left(s"VM timed out after $maxSteps steps. This should not happen with the supplied tests")
|
||||
case Success => Right(binary, trace)
|
||||
}
|
||||
}
|
||||
|
||||
def labelMapReverse = labelMap.toList.map(_.swap).toMap
|
||||
}
|
||||
|
||||
|
||||
def lookupReg(s: String): Option[Int] = {
|
||||
val regMap = Map(
|
||||
"x0" -> 0,
|
||||
"x1" -> 1,
|
||||
"x2" -> 2,
|
||||
"x3" -> 3,
|
||||
"x4" -> 4,
|
||||
"x5" -> 5,
|
||||
"x6" -> 6,
|
||||
"x7" -> 7,
|
||||
"x8" -> 8,
|
||||
"x9" -> 9,
|
||||
"x10" -> 10,
|
||||
"x11" -> 11,
|
||||
"x12" -> 12,
|
||||
"x13" -> 13,
|
||||
"x14" -> 14,
|
||||
"x15" -> 15,
|
||||
"x16" -> 16,
|
||||
"x17" -> 17,
|
||||
"x18" -> 18,
|
||||
"x19" -> 19,
|
||||
"x20" -> 20,
|
||||
"x21" -> 21,
|
||||
"x22" -> 22,
|
||||
"x23" -> 23,
|
||||
"x24" -> 24,
|
||||
"x25" -> 25,
|
||||
"x26" -> 26,
|
||||
"x27" -> 27,
|
||||
"x28" -> 28,
|
||||
"x29" -> 29,
|
||||
"x30" -> 30,
|
||||
"x31" -> 31,
|
||||
"zero" -> 0,
|
||||
"ra" -> 1,
|
||||
"sp" -> 2,
|
||||
"gp" -> 3,
|
||||
"tp" -> 4,
|
||||
"t0" -> 5,
|
||||
"t1" -> 6,
|
||||
"t2" -> 7,
|
||||
"s0" -> 8,
|
||||
"fp" -> 8,
|
||||
"s1" -> 9,
|
||||
"a0" -> 10,
|
||||
"a1" -> 11,
|
||||
"a2" -> 12,
|
||||
"a3" -> 13,
|
||||
"a4" -> 14,
|
||||
"a5" -> 15,
|
||||
"a6" -> 16,
|
||||
"a7" -> 17,
|
||||
"s2" -> 18,
|
||||
"s3" -> 19,
|
||||
"s4" -> 20,
|
||||
"s5" -> 21,
|
||||
"s6" -> 22,
|
||||
"s7" -> 23,
|
||||
"s8" -> 24,
|
||||
"s9" -> 25,
|
||||
"s10" -> 26,
|
||||
"s11" -> 27,
|
||||
"t3" -> 28,
|
||||
"t4" -> 29,
|
||||
"t5" -> 30,
|
||||
"t6" -> 31)
|
||||
|
||||
regMap.lift(s)
|
||||
}
|
||||
}
|
184
src/test/scala/RISCV/LogParser.scala
Normal file
184
src/test/scala/RISCV/LogParser.scala
Normal file
|
@ -0,0 +1,184 @@
|
|||
package FiveStage
|
||||
import Data._
|
||||
import fileUtils.say
|
||||
import PrintUtils._
|
||||
|
||||
/**
|
||||
* Helpers for comparing VM and chisel logs
|
||||
*/
|
||||
object LogParser {
|
||||
|
||||
|
||||
/**
|
||||
* Peeks ahead at the chisel log to see if an expected jump is taken.
|
||||
*/
|
||||
def fetchUntilJumpTarget(addr: Addr, chiselLog: List[CircuitTrace]): Option[List[CircuitTrace]] = {
|
||||
val (head, tail) = chiselLog.splitAt(4) // very arbitrary choice
|
||||
val pruned: List[CircuitTrace] = head.dropWhile{ case(myAddr, _) => myAddr != addr }
|
||||
pruned.headOption.map(_ => pruned ::: tail)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetches a basic block of VM execution trace
|
||||
*/
|
||||
def splitToBlocks(vmTrace: List[ExecutionTraceEvent]): List[List[ExecutionTraceEvent]] =
|
||||
vmTrace.splitAtPred{ case(current, next) =>
|
||||
!(next.pc == current.pc + Addr(4))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches a basic block of chisel execution trace
|
||||
*/
|
||||
def splitToBlocksChisel(chiselTrace: List[CircuitTrace]): List[List[CircuitTrace]] =
|
||||
chiselTrace.splitAtPred{ case((current, _), (next, _)) =>
|
||||
!((next == current + Addr(4)) || (next == current))
|
||||
}
|
||||
|
||||
|
||||
def guessBlockName(trace: List[Addr], labelMap: Map[Addr, Label]): String = trace.headOption.map(x => labelMap.lift(x)
|
||||
.getOrElse(labelMap.lift(trace.head - Addr(4)).getOrElse("UNKNOWN (return jump or misjump)"))).getOrElse("UNKNOWN")
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to merge blocks, patching up when mismatches occur
|
||||
* Fails when branchpredictor misjumps, feel free to send a PR
|
||||
*/
|
||||
type BlockList = List[(List[ExecutionTraceEvent], List[CircuitTrace])]
|
||||
|
||||
def mergeTraces(vmTrace: List[ExecutionTraceEvent], chiselTrace: List[CircuitTrace]): BlockList = {
|
||||
def helper(acc: BlockList, blocs: (List[List[ExecutionTraceEvent]], List[List[CircuitTrace]])): BlockList = blocs match {
|
||||
|
||||
case (vmBlock :: vmTail, chiselBlock :: chiselTail) if (vmBlock.head.pc == chiselBlock.head._1) =>
|
||||
helper((vmBlock, chiselBlock) :: acc, ((vmTail, chiselTail)))
|
||||
|
||||
case (vmBlock :: vmTail, chiselBlock :: chiselTail) =>
|
||||
helper((Nil, chiselBlock) :: acc, ((vmBlock :: vmTail, chiselTail)))
|
||||
|
||||
case (Nil, chiselBlock :: chiselTail) =>
|
||||
helper((Nil, chiselBlock) :: acc, ((Nil, chiselTail)))
|
||||
|
||||
case (vmBlock :: vmTail, Nil) =>
|
||||
helper((vmBlock, Nil) :: acc, ((vmTail, Nil)))
|
||||
|
||||
case _ => acc.reverse
|
||||
}
|
||||
helper(Nil, (splitToBlocks(vmTrace), splitToBlocksChisel(chiselTrace)))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares register update logs
|
||||
*/
|
||||
def compareRegs(vmTrace: List[ExecutionTraceEvent], chiselTrace: List[CircuitTrace]): Option[String] = {
|
||||
val vmRegUpdates = vmTrace.zipWithIndex
|
||||
.flatMap{ case (e, step) => e.event.toList.map(y => (step, e.pc, y))}
|
||||
.collect{ case (step: Int, addr: Addr, x: RegUpdate) => (step, addr, x) }
|
||||
|
||||
val chiselRegUpdates = chiselTrace.zipWithIndex
|
||||
.flatMap{ case (e, step) => e._2.map(y => (step, e._1, y))}
|
||||
.collect{ case (step: Int, addr: Addr, x: ChiselRegEvent) => (step, addr, x) }
|
||||
|
||||
val errors = (vmRegUpdates zip chiselRegUpdates).map{
|
||||
case((_, _, vmUpdate), (_, _, chiselUpdate)) if ((vmUpdate.reg == chiselUpdate.reg) && (vmUpdate.word == chiselUpdate.word)) => {
|
||||
None
|
||||
}
|
||||
|
||||
case((vmStep, vmAddr, vmUpdate), (chiselStep, chiselAddr, chiselUpdate)) => {
|
||||
val errorString = s"Register update mismatch.\n" ++
|
||||
s"VM: At step $vmStep, at address ${vmAddr.show}, the VM got ${vmUpdate.show}\n" ++
|
||||
s"Circuit: At step $chiselStep, at address ${chiselAddr.show}, the circuit got ${chiselUpdate.show}"
|
||||
Some(errorString)
|
||||
}
|
||||
}
|
||||
|
||||
val error = errors.collect{ case Some(x) => x }.headOption
|
||||
|
||||
|
||||
val lengthMismatch: Option[String] = (vmRegUpdates, chiselRegUpdates) match {
|
||||
case (h :: t, Nil) => Some(s"Your design performed no reg updates. First expected update was at VM step ${h._1}, PC: ${h._2.show}, ${h._3.show}")
|
||||
case (Nil, h :: t) => Some(s"Your design performed reg updates, but the VM did not. First update was at step ${h._1}, PC: ${h._2.show}, ${h._3.show}")
|
||||
case (hVM :: tVM, hC :: tC) if (tVM.size > tC.size) => {
|
||||
|
||||
val VMremainder = tVM.drop(tC.size)
|
||||
|
||||
val errorString =
|
||||
s"VM performed more reg updates than your design.\n" ++
|
||||
s"Your design performed ${chiselRegUpdates.size} updates before terminating, while the VM performed ${vmRegUpdates.size} updates.\n" ++
|
||||
s"The first update your design missed happened at VM step ${VMremainder.head._1}, PC: ${VMremainder.head._2.show} and was ${VMremainder.head._3.show}"
|
||||
|
||||
Some(errorString)
|
||||
}
|
||||
case (hVM :: tVM, hC :: tC) if (tVM.size < tC.size) => {
|
||||
val ChiselRemainder = tC.drop(tVM.size)
|
||||
|
||||
val errorString =
|
||||
s"Your design performed more reg updates than the VM.\n" ++
|
||||
s"Your design performed ${chiselRegUpdates.size} updates before terminating, while the VM performed ${vmRegUpdates.size} updates.\n" ++
|
||||
s"The first spurious update your design did happened at cycle ${ChiselRemainder.head._1}, PC: ${ChiselRemainder.head._2.show} and was ${ChiselRemainder.head._3.show}"
|
||||
|
||||
Some(errorString)
|
||||
}
|
||||
|
||||
case _ => None
|
||||
}
|
||||
|
||||
|
||||
(error :: lengthMismatch :: Nil).flatten.headOption
|
||||
}
|
||||
|
||||
|
||||
def compareMem(vmTrace: List[ExecutionTraceEvent], chiselTrace: List[CircuitTrace]): Option[String] = {
|
||||
val vmMemUpdates = vmTrace.zipWithIndex
|
||||
.flatMap{ case (e, step) => e.event.toList.map(y => (step, e.pc, y))}
|
||||
.collect{ case (step: Int, addr: Addr, x: MemWrite) => (step, addr, x) }
|
||||
|
||||
val chiselMemUpdates = chiselTrace.zipWithIndex
|
||||
.flatMap{ case (e, step) => e._2.map(y => (step, e._1, y))}
|
||||
.collect{ case (step: Int, addr: Addr, x: ChiselMemWriteEvent) => (step, addr, x) }
|
||||
|
||||
val error = (vmMemUpdates zip chiselMemUpdates).map{
|
||||
case((_, _, vmUpdate), (_, _, chiselUpdate)) if ((vmUpdate.addr == chiselUpdate.memAddr) && (vmUpdate.word == chiselUpdate.word)) =>
|
||||
None
|
||||
case((vmStep, vmAddr, vmUpdate), (chiselStep, chiselAddr, chiselUpdate)) => {
|
||||
val errorString = s"Mem update mismatch.\n" ++
|
||||
s"VM: At step $vmStep, at address ${vmAddr.show}, the VM got ${vmUpdate.show}\n" ++
|
||||
s"Circuit: At step $chiselStep, at address ${chiselAddr.show}, the circuit got ${chiselUpdate.show}"
|
||||
Some(errorString)
|
||||
}
|
||||
}.collect{ case Some(x) => x }.headOption
|
||||
|
||||
|
||||
val lengthMismatch = (vmMemUpdates, chiselMemUpdates) match {
|
||||
case (h :: t, Nil) => Some(s"Your design performed no mem updates. First expected update was at VM step ${h._1}, PC: ${h._2}, ${h._3}")
|
||||
case (Nil, h :: t) => Some(s"Your design performed mem updates, but the VM did not. First spurious update was at step ${h._1}, PC: ${h._2}, ${h._3}")
|
||||
case (hVM :: tVM, hC :: tC) if (tVM.size > tC.size) => {
|
||||
|
||||
val VMremainder = tVM.drop(tC.size)
|
||||
|
||||
val errorString =
|
||||
s"VM performed more mem updates than your design.\n" ++
|
||||
s"Your design performed ${chiselMemUpdates.size} updates before terminating, while the VM performed ${vmMemUpdates.size} updates.\n" ++
|
||||
s"The first update your design missed happened at VM step ${VMremainder.head._1}, PC: ${VMremainder.head._2} and was ${VMremainder.head._3}"
|
||||
|
||||
Some(errorString)
|
||||
}
|
||||
case (hVM :: tVM, hC :: tC) if (tVM.size < tC.size) => {
|
||||
val ChiselRemainder = tC.drop(tVM.size)
|
||||
|
||||
val errorString =
|
||||
s"Your design performed more mem updates than the VM.\n" ++
|
||||
s"Your design performed ${chiselMemUpdates.size} updates before terminating, while the VM performed ${vmMemUpdates.size} updates.\n" ++
|
||||
s"The first spurious update your design did happened at cycle ${ChiselRemainder.head._1}, PC: ${ChiselRemainder.head._2} and was ${ChiselRemainder.head._3}"
|
||||
|
||||
Some(errorString)
|
||||
}
|
||||
|
||||
case _ => None
|
||||
}
|
||||
|
||||
(error :: lengthMismatch :: Nil).flatten.headOption
|
||||
}
|
||||
}
|
124
src/test/scala/RISCV/Ops.scala
Normal file
124
src/test/scala/RISCV/Ops.scala
Normal file
|
@ -0,0 +1,124 @@
|
|||
package FiveStage
|
||||
import cats.implicits._
|
||||
import fileUtils._
|
||||
|
||||
import Data._
|
||||
import PrintUtils._
|
||||
|
||||
object Ops {
|
||||
|
||||
sealed trait Op extends RegLayout
|
||||
|
||||
sealed trait RegLayout
|
||||
sealed trait RType extends RegLayout { def rd: Reg; def rs1: Reg; def rs2: Reg }
|
||||
sealed trait IType extends RegLayout { def rd: Reg; def rs1: Reg; }
|
||||
sealed trait SType extends RegLayout { def rs1: Reg; def rs2: Reg }
|
||||
sealed trait UType extends RegLayout { def rd: Reg; }
|
||||
|
||||
sealed trait ImmType
|
||||
sealed trait NoImmediate extends ImmType
|
||||
sealed trait IImmediate extends ImmType
|
||||
sealed trait SImmediate extends ImmType
|
||||
sealed trait BImmediate extends ImmType
|
||||
sealed trait UImmediate extends ImmType
|
||||
sealed trait JImmediate extends ImmType
|
||||
sealed trait ShiftImmediate extends ImmType
|
||||
|
||||
|
||||
sealed trait Comparison {
|
||||
def run(rs1Val: Int, rs2Val: Int): Boolean
|
||||
}
|
||||
case object EQ extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = rs1Val == rs2Val }
|
||||
case object NE extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = rs1Val != rs2Val }
|
||||
case object GE extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = rs1Val >= rs2Val }
|
||||
case object LT extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = rs1Val < rs2Val }
|
||||
case object GEU extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = !(rs1Val `u>` rs2Val) }
|
||||
case object LTU extends Comparison { def run(rs1Val: Int, rs2Val: Int): Boolean = rs1Val `u>` rs2Val }
|
||||
|
||||
case class Branch(rs1: Reg, rs2: Reg, dst: Label, comp: Comparison) extends Op with SType
|
||||
object Branch{
|
||||
def beq( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, EQ)
|
||||
def bne( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, NE)
|
||||
def blt( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, LT)
|
||||
def bge( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, GE)
|
||||
def bltu(rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, LTU)
|
||||
def bgeu(rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs1), Reg(rs2), dst, GEU)
|
||||
|
||||
def ble( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs2), Reg(rs1), dst, GE)
|
||||
def bgt( rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs2), Reg(rs1), dst, LT)
|
||||
def bleu(rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs2), Reg(rs1), dst, GEU)
|
||||
def bgtu(rs1: Int, rs2: Int, dst: Label) = Branch(Reg(rs2), Reg(rs1), dst, LTU)
|
||||
|
||||
def beqz(rs1: Int, dst: Label) = Branch(Reg(rs1), Reg(0), dst, EQ)
|
||||
def bnez(rs1: Int, dst: Label) = Branch(Reg(rs1), Reg(0), dst, NE)
|
||||
def blez(rs1: Int, dst: Label) = Branch(Reg(rs1), Reg(0), dst, LT)
|
||||
}
|
||||
|
||||
sealed trait someDecorator
|
||||
sealed trait ArithOp {
|
||||
def run(operand1: Int, operand2: Int): Int
|
||||
}
|
||||
case object ADD extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 + operand2 }
|
||||
case object SUB extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 - operand2 }
|
||||
case object OR extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 | operand2 }
|
||||
case object XOR extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 ^ operand2 }
|
||||
case object AND extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 & operand2 }
|
||||
case object SLL extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 << operand2 }
|
||||
case object SRL extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 >>> operand2 }
|
||||
case object SRA extends ArithOp { def run(operand1: Int, operand2: Int): Int = operand1 >> operand2 }
|
||||
case object SLT extends ArithOp { def run(operand1: Int, operand2: Int): Int = if(operand2 > operand1) 1 else 0 }
|
||||
case object SLTU extends ArithOp { def run(operand1: Int, operand2: Int): Int = if(operand2 `u>` operand1) 1 else 0 }
|
||||
|
||||
case class Arith(rd: Reg, rs1: Reg, rs2: Reg, op: ArithOp) extends Op with RType
|
||||
object Arith {
|
||||
def add( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), ADD)
|
||||
def sub( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SUB)
|
||||
def or( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), OR)
|
||||
def xor( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), XOR)
|
||||
def and( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), AND)
|
||||
def sll( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SLL)
|
||||
def srl( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SRL)
|
||||
def sra( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SRA)
|
||||
def slt( rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SLT)
|
||||
def sltu(rd: Int, rs1: Int, rs2: Int) = Arith(Reg(rd), Reg(rs1), Reg(rs2), SLTU)
|
||||
}
|
||||
|
||||
def NOP = ArithImm.nop
|
||||
|
||||
|
||||
|
||||
case class ArithImm(rd: Reg, rs1: Reg, imm: Imm, op: ArithOp) extends Op with IType
|
||||
object ArithImm {
|
||||
def add( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), ADD)
|
||||
def or( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), OR)
|
||||
def xor( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), XOR)
|
||||
def and( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), AND)
|
||||
def sll( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), SLL)
|
||||
def srl( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), SRL)
|
||||
def sra( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), SRA)
|
||||
def slt( rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), SLT)
|
||||
def sltu(rd: Int, rs1: Int, imm: Int) = ArithImm(Reg(rd), Reg(rs1), Imm(imm), SLTU)
|
||||
def nop = add(0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
case class LUI(rd: Reg, imm: Imm) extends Op with UType
|
||||
case class AUIPC(rd: Reg, imm: Imm) extends Op with UType
|
||||
case class SW(rs2: Reg, rs1: Reg, offset: Imm) extends Op with SType
|
||||
case class LW(rd: Reg, rs1: Reg, offset: Imm) extends Op with IType
|
||||
|
||||
case class JALR(rd: Reg, rs1: Reg, dst: String) extends Op with IType
|
||||
case class JAL(rd: Reg, dst: String) extends Op with UType
|
||||
|
||||
|
||||
object LUI { def apply(rd: Int, imm: Int): LUI = LUI(Reg(rd), Imm(imm)) }
|
||||
object AUIPC { def apply(rd: Int, imm: Int): AUIPC = AUIPC(Reg(rd), Imm(imm)) }
|
||||
object SW { def apply(rs2: Int, rs1: Int, offset: Int): SW = SW(Reg(rs2), Reg(rs1), Imm(offset)) }
|
||||
object LW { def apply(rd: Int, rs1: Int, offset: Int): LW = LW(Reg(rd), Reg(rs1), Imm(offset)) }
|
||||
|
||||
object JAL{ def apply(rd: Int, dst: String): JAL = JAL(Reg(rd), dst) }
|
||||
object JALR{ def apply(rd: Int, rs1: Int, dst: String): JALR = JALR(Reg(rd), Reg(rs1), dst) }
|
||||
|
||||
// This op should not be assembled, but will for the sake of simplicity be rendered as a NOP
|
||||
case object DONE extends Op with IType { val rd = Reg(0); val rs1 = Reg(0) }
|
||||
}
|
357
src/test/scala/RISCV/Parser.scala
Normal file
357
src/test/scala/RISCV/Parser.scala
Normal file
|
@ -0,0 +1,357 @@
|
|||
package FiveStage
|
||||
import atto._, Atto._, syntax.refined._
|
||||
import eu.timepit.refined.numeric._
|
||||
import fileUtils.say
|
||||
|
||||
import Ops._
|
||||
import Data._
|
||||
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
|
||||
object Parser {
|
||||
|
||||
def hex : Parser[Int] = string("0x") ~> many1(hexDigit).map{ ds =>
|
||||
val bi = Integer.parseUnsignedInt(new String(ds.toList.toArray), 16)
|
||||
bi.toInt
|
||||
}
|
||||
|
||||
def labelDest : Parser[Label] = (takeWhile(_ != ':') <~ char(':'))
|
||||
def label : Parser[Label] = takeWhile(_ != ' ')
|
||||
def reg : Parser[Int] = takeWhile(x => (x != ',' && x != ')')).map(lookupReg).attempt
|
||||
def sep : Parser[Unit] = many(whitespace) *> char(',') *> many(whitespace).void
|
||||
|
||||
def branch : (Parser[Int], Parser[Int], Parser[String]) = (reg <~ sep, reg <~ sep, label)
|
||||
def branchZ : (Parser[Int], Parser[String]) = (reg <~ sep, label)
|
||||
|
||||
def arith : (Parser[Int], Parser[Int], Parser[Int]) = (reg <~ sep, reg <~ sep, reg)
|
||||
def arithImm : (Parser[Int], Parser[Int], Parser[Int]) = (reg <~ sep, reg <~ sep, hex | int)
|
||||
|
||||
def stringWs(s: String) : Parser[String] = many(whitespace) ~> string(s) <~ many1(whitespace)
|
||||
|
||||
val singleInstruction: Parser[Op] = List(
|
||||
////////////////////////////////////////////
|
||||
//// Branches
|
||||
stringWs("beq") ~> branch.mapN{Branch.beq},
|
||||
stringWs("bne") ~> branch.mapN{Branch.bne},
|
||||
stringWs("blt") ~> branch.mapN{Branch.blt},
|
||||
stringWs("bge") ~> branch.mapN{Branch.bge},
|
||||
stringWs("bltu") ~> branch.mapN{Branch.bltu},
|
||||
stringWs("bgeu") ~> branch.mapN{Branch.bgtu},
|
||||
|
||||
// pseudos:
|
||||
stringWs("ble") ~> branch.mapN{Branch.ble},
|
||||
stringWs("bgt") ~> branch.mapN{Branch.bgt},
|
||||
stringWs("bleu") ~> branch.mapN{Branch.bleu},
|
||||
stringWs("bgtu") ~> branch.mapN{Branch.bgtu},
|
||||
|
||||
// Introduce zero
|
||||
stringWs("bnez") ~> branchZ.mapN{Branch.bnez},
|
||||
stringWs("beqz") ~> branchZ.mapN{Branch.beqz},
|
||||
stringWs("blez") ~> branchZ.mapN{Branch.blez},
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// Arith
|
||||
stringWs("add") ~> arith.mapN{Arith.add},
|
||||
stringWs("sub") ~> arith.mapN{Arith.sub},
|
||||
stringWs("or") ~> arith.mapN{Arith.or},
|
||||
stringWs("xor") ~> arith.mapN{Arith.xor},
|
||||
stringWs("and") ~> arith.mapN{Arith.and},
|
||||
|
||||
stringWs("sll") ~> arith.mapN{Arith.sll},
|
||||
stringWs("srl") ~> arith.mapN{Arith.srl},
|
||||
stringWs("sra") ~> arith.mapN{Arith.sra},
|
||||
|
||||
stringWs("slt") ~> arith.mapN{Arith.slt},
|
||||
stringWs("sltu") ~> arith.mapN{Arith.sltu},
|
||||
|
||||
// pseudos
|
||||
stringWs("mv") ~> (reg <~ sep, reg, ok(0)).mapN{Arith.add},
|
||||
stringWs("nop") ~> (ok(0), ok(0), ok(0)).mapN{Arith.add},
|
||||
|
||||
// Check if rs1 is not equal to 0.
|
||||
// snez rd, rs1 => sltu rd, zero, rs1
|
||||
stringWs("snez") ~> (reg <~ sep, ok(0), reg).mapN{Arith.sltu},
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// Arith Imm
|
||||
stringWs("addi") ~> arithImm.mapN{ArithImm.add},
|
||||
stringWs("ori") ~> arithImm.mapN{ArithImm.or},
|
||||
stringWs("xori") ~> arithImm.mapN{ArithImm.xor},
|
||||
stringWs("andi") ~> arithImm.mapN{ArithImm.and},
|
||||
|
||||
stringWs("slli") ~> arithImm.mapN{ArithImm.sll},
|
||||
stringWs("srli") ~> arithImm.mapN{ArithImm.srl},
|
||||
stringWs("srai") ~> arithImm.mapN{ArithImm.sra},
|
||||
|
||||
stringWs("slti") ~> arithImm.mapN{ArithImm.slt},
|
||||
stringWs("sltui") ~> arithImm.mapN{ArithImm.sltu},
|
||||
|
||||
// pseudos
|
||||
stringWs("not") ~> (reg <~ sep, reg, ok(-1)).mapN{ArithImm.xor},
|
||||
|
||||
// Check if rs1 is less than 1. Only 0 is less than 1 when using unsigned comparison
|
||||
// seqz rd, rs1 => sltiu rd, rs1, 1
|
||||
stringWs("seqz") ~> (reg <~ sep, reg, ok(1)).mapN{ArithImm.sltu},
|
||||
|
||||
stringWs("li") ~> (reg ~ sep ~ int).collect{
|
||||
case((a, b), c) if (c.nBitsS <= 12) => ArithImm.add(a, 0, c)
|
||||
},
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// Jumps
|
||||
stringWs("jalr") ~> (reg <~ sep, reg <~ sep, label).mapN{JALR.apply},
|
||||
stringWs("jal") ~> (reg <~ sep, label).mapN{JAL.apply},
|
||||
|
||||
// pseudos
|
||||
// JAL with ra as rd automatically chosen.
|
||||
stringWs("call") ~> label.map(label => JAL(regNames.ra, label)),
|
||||
|
||||
// For jr we don't care about where we jumped from.
|
||||
stringWs("jr") ~> reg.map(r => JALR(0, r, "zero")),
|
||||
|
||||
// As jr, but with a label rather than a register.
|
||||
stringWs("j") ~> label.map(label => JAL(0, label)),
|
||||
many(whitespace) ~> string("ret") ~> ok(JALR(0, regNames.ra, "zero")),
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// load/store
|
||||
stringWs("sw") ~> (reg <~ sep, int <~ char('('), reg <~ char(')')).mapN{case (rs2, offset, rs1) => SW(rs2, rs1, offset)},
|
||||
stringWs("lw") ~> (reg <~ sep, int <~ char('('), reg <~ char(')')).mapN{case (rd, offset, rs1) => LW(rd, rs1, offset)},
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// others
|
||||
stringWs("auipc") ~> (reg <~ sep, int).mapN{AUIPC.apply},
|
||||
stringWs("lui") ~> (reg <~ sep, int).mapN{LUI.apply},
|
||||
|
||||
many(whitespace) ~> string("nop") ~> ok(Arith.add(0, 0, 0)),
|
||||
many(whitespace) ~> string("done") ~> ok(DONE),
|
||||
// stringWs("done") ~> ok(DONE),
|
||||
|
||||
).map(_.widen[Op]).reduce(_|_)
|
||||
|
||||
|
||||
// def getShiftsHalfWord(offset: Int): (Int, Int) = (offset % 4) match {
|
||||
// case 0 => (16, 16)
|
||||
// case 1 => (
|
||||
// }
|
||||
|
||||
val multipleInstructions: Parser[List[Op]] = List(
|
||||
stringWs("li") ~> (reg <~ sep, int.map(_.splitLoHi(12))).mapN{ case(rd, (lo, hi)) => List(
|
||||
LUI(rd, hi),
|
||||
ArithImm.add(rd, 0, lo)
|
||||
)}.map(_.widen[Op]),
|
||||
|
||||
// NOTE: THESE ARE NOT PSEUDO-OPS IN RISV32I!
|
||||
// NOTE: USES A SPECIAL REGISTER
|
||||
stringWs("lh") ~> (reg <~ sep, int <~ char('('), reg <~ char(')')).mapN{
|
||||
case (rd, offset, rs1) if (offset % 4 == 3) => {
|
||||
val placeHolder = if(rd == Reg("a0").value) Reg("a1").value else Reg("a0").value
|
||||
List(
|
||||
SW(placeHolder, 0, 2048),
|
||||
LW(placeHolder, rs1.value, (offset & 0xFFFFFF1C)),
|
||||
LW(rd.value, rs1.value, (offset & 0xFFFFFF1C) + 4),
|
||||
ArithImm.sra(placeHolder, placeHolder, 24),
|
||||
ArithImm.sll(rd.value, rd.value, 24),
|
||||
ArithImm.sra(rd.value, rd.value, 16),
|
||||
Arith.add(rd, rd, placeHolder),
|
||||
LW(placeHolder, 0, 2048)).reverse
|
||||
}
|
||||
case (rd, offset, rs1) if (offset % 4 == 2) => {
|
||||
List(
|
||||
LW(rd, rs1, (offset & 0xFFFFFF1C)),
|
||||
ArithImm.sra(rd, rd, 16)
|
||||
).reverse
|
||||
}
|
||||
|
||||
case (rd, offset, rs1) => {
|
||||
val leftShift = if((offset % 4) == 0) 16 else 8
|
||||
List(
|
||||
LW(rd, rs1, (offset & 0xFFFFFF1C)),
|
||||
ArithImm.sll(rd, rd, leftShift),
|
||||
ArithImm.sra(rd, rd, 16),
|
||||
).reverse
|
||||
}
|
||||
}.map(_.widen[Op]),
|
||||
).reduce(_|_)
|
||||
|
||||
|
||||
val instruction = singleInstruction.map(List(_)) | multipleInstructions
|
||||
|
||||
val setting = List(
|
||||
char('#') ~> string("regset") ~> many1(whitespace) ~> (reg.map(Reg.apply) <~ sep, hex | int).mapN{REGSET.apply},
|
||||
char('#') ~> string("memset") ~> many1(whitespace) ~> ((hex | int).map(Addr.apply) <~ sep, hex | int).mapN{MEMSET.apply}
|
||||
).map(_.widen[TestSetting]).reduce(_|_)
|
||||
|
||||
|
||||
def parseProgram(p: List[String], testOptions: TestOptions): Either[String, Program] = {
|
||||
|
||||
val all = setting || (instruction || labelDest)
|
||||
|
||||
/**
|
||||
* The foldhelper represents a traversal through a RISC-V program.
|
||||
*
|
||||
* When it sees an op it records the operation and appends the source line and its location.
|
||||
* If it is in nopPad mode it will also insert NOPs between the parsed ops.
|
||||
* After appending ops the address counter is bumbed accordingly
|
||||
*
|
||||
* When it sees a label destination it checks what the current addres counter is at and creates
|
||||
* a link to this address.
|
||||
*
|
||||
* When it sees a parse error it simply stores the error and keeps going, allowing you to get every error
|
||||
* (This works for an ASM program since each line is independent)
|
||||
*
|
||||
* Lastly, when it sees a test setting it appends that test setting.
|
||||
*
|
||||
* The reason everything is treated all-in-one is to make it easier to ensure that everything is parsed.
|
||||
* If there were separate parsers for ops, labels and settings it would be difficult to find out if errors
|
||||
* were simply of the wrong type or a legit error.
|
||||
* This is not set in stone, if you're re-architecturing the code maybe it's better to separate parsers?
|
||||
* Or maybe have multiple passes? Up to you!
|
||||
*/
|
||||
case class FoldHelper(
|
||||
settings : List[TestSetting],
|
||||
ops : List[SourceInfo[Op]],
|
||||
labelMap : Map[Label, Addr],
|
||||
errors : List[String],
|
||||
addrCount : Int){
|
||||
def addSettings (t: TestSetting): FoldHelper = copy(settings = t :: settings)
|
||||
def addErrors (t: String): FoldHelper = copy(errors = t :: errors)
|
||||
def addLabelMap (t: Label): FoldHelper = copy(labelMap = labelMap + (t -> Addr(addrCount)))
|
||||
def addOps (t: List[SourceInfo[Op]]): FoldHelper = {
|
||||
if(testOptions.nopPadded){
|
||||
copy(
|
||||
ops = t.flatMap(x => (x :: List.fill(4)(SourceInfo("inserted NOP", NOP).widen[Op]))).reverse ::: ops,
|
||||
addrCount = addrCount + t.size*4*5)
|
||||
}
|
||||
else {
|
||||
copy(ops = t ::: ops, addrCount = addrCount + t.size*4)
|
||||
}
|
||||
}
|
||||
def program: Either[String, (List[TestSetting], List[SourceInfo[Op]], Map[Label, Addr])] = {
|
||||
/**
|
||||
* There are two possible ways for a program to successfully terminate, either by explicitly executing
|
||||
* a DONE instruction, or by returning from the main method.
|
||||
* In the latter case it is necessary to preload the return address register such that the return instruction
|
||||
* jumps to a predetermined special done address
|
||||
*/
|
||||
val hasDONEinstruction = ops.map(_.run._2).contains(DONE)
|
||||
val done = copy(settings = REGSET(Reg("sp"), 1024) :: settings, ops = ops.reverse, errors = errors.reverse)
|
||||
|
||||
val withReturnAddress = if(hasDONEinstruction){
|
||||
done
|
||||
}
|
||||
else
|
||||
done.copy(settings = REGSET(Reg("ra"), 0xEB1CEB1C) :: done.settings) // now that's what I call EPIC
|
||||
|
||||
Either.cond(errors.isEmpty, (withReturnAddress.settings, done.ops, labelMap), done.errors).left.map(errors =>
|
||||
s"Parser errors in ${testOptions.testName}:\n" + errors.mkString("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def foldHelper(
|
||||
acc: FoldHelper,
|
||||
program: List[(Int, String)]): FoldHelper = program match {
|
||||
case Nil => acc
|
||||
case (lineNo, line) :: t => {
|
||||
if(line.isEmpty)
|
||||
foldHelper(acc, t)
|
||||
else {
|
||||
val next = all.parse(line).done.either match {
|
||||
case Left(parseError) => acc.addErrors(f"$lineNo%3d" +s":$line\t$parseError")
|
||||
case Right(Left(setting)) => acc.addSettings(setting)
|
||||
case Right(Right(Right(label))) => acc.addLabelMap(label)
|
||||
case Right(Right(Left(ops))) => acc.addOps(ops.map(op => SourceInfo(s"${lineNo.toString.padTo(3, ' ')}:\t$line", op)))
|
||||
}
|
||||
foldHelper(next, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val results = foldHelper(FoldHelper(Nil, Nil, Map("zero" -> Addr(0)), Nil, 0), p.zipWithIndex.map(_.swap))
|
||||
|
||||
results.program.map{ case(settings, ops, labelMap) => Program(ops, settings, labelMap) }
|
||||
}
|
||||
|
||||
|
||||
def lookupReg(s: String): Int = {
|
||||
val regMap = Map(
|
||||
"x0" -> 0,
|
||||
"x1" -> 1,
|
||||
"x2" -> 2,
|
||||
"x3" -> 3,
|
||||
"x4" -> 4,
|
||||
"x5" -> 5,
|
||||
"x6" -> 6,
|
||||
"x7" -> 7,
|
||||
"x8" -> 8,
|
||||
"x9" -> 9,
|
||||
"x10" -> 10,
|
||||
"x11" -> 11,
|
||||
"x12" -> 12,
|
||||
"x13" -> 13,
|
||||
"x14" -> 14,
|
||||
"x15" -> 15,
|
||||
"x16" -> 16,
|
||||
"x17" -> 17,
|
||||
"x18" -> 18,
|
||||
"x19" -> 19,
|
||||
"x20" -> 20,
|
||||
"x21" -> 21,
|
||||
"x22" -> 22,
|
||||
"x23" -> 23,
|
||||
"x24" -> 24,
|
||||
"x25" -> 25,
|
||||
"x26" -> 26,
|
||||
"x27" -> 27,
|
||||
"x28" -> 28,
|
||||
"x29" -> 29,
|
||||
"x30" -> 30,
|
||||
"x31" -> 31,
|
||||
"zero" -> 0,
|
||||
"ra" -> 1,
|
||||
"sp" -> 2,
|
||||
"gp" -> 3,
|
||||
"tp" -> 4,
|
||||
"t0" -> 5,
|
||||
"t1" -> 6,
|
||||
"t2" -> 7,
|
||||
"s0" -> 8,
|
||||
"fp" -> 8,
|
||||
"s1" -> 9,
|
||||
"a0" -> 10,
|
||||
"a1" -> 11,
|
||||
"a2" -> 12,
|
||||
"a3" -> 13,
|
||||
"a4" -> 14,
|
||||
"a5" -> 15,
|
||||
"a6" -> 16,
|
||||
"a7" -> 17,
|
||||
"s2" -> 18,
|
||||
"s3" -> 19,
|
||||
"s4" -> 20,
|
||||
"s5" -> 21,
|
||||
"s6" -> 22,
|
||||
"s7" -> 23,
|
||||
"s8" -> 24,
|
||||
"s9" -> 25,
|
||||
"s10" -> 26,
|
||||
"s11" -> 27,
|
||||
"t3" -> 28,
|
||||
"t4" -> 29,
|
||||
"t5" -> 30,
|
||||
"t6" -> 31)
|
||||
|
||||
regMap(s)
|
||||
}
|
||||
}
|
171
src/test/scala/RISCV/VM.scala
Normal file
171
src/test/scala/RISCV/VM.scala
Normal file
|
@ -0,0 +1,171 @@
|
|||
package FiveStage
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
import fileUtils._
|
||||
|
||||
import Data._
|
||||
import Ops._
|
||||
|
||||
import PrintUtils._
|
||||
|
||||
sealed trait Finished
|
||||
case object Success extends Finished
|
||||
case object Timeout extends Finished
|
||||
case class Failed(s: String, addr: Addr) extends Finished
|
||||
|
||||
case class VM(
|
||||
dmem : DMem,
|
||||
imem : Map[Addr, Op],
|
||||
regs : Regs,
|
||||
pc : Addr,
|
||||
labelMap : Map[Label, Addr]){
|
||||
def stepInstruction: Either[Finished, ExecutionTrace[VM]] = {
|
||||
if (pc.value == 0xEB1CEB1C) Left(Success)
|
||||
else getOp flatMap {
|
||||
case op: Branch => executeBranch(op)
|
||||
case op: Arith => executeArith(op)
|
||||
case op: ArithImm => executeArithImm(op)
|
||||
case op: AUIPC => executeAUIPC(op)
|
||||
case op: LUI => executeLUI(op)
|
||||
case op: JALR => executeJALR(op)
|
||||
case op: JAL => executeJAL(op)
|
||||
case op: LW => executeLW(op)
|
||||
case op: SW => executeSW(op)
|
||||
case DONE => Left(Success)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private def executeBranch(op: Branch) = {
|
||||
getAddr(op.dst).map{ addr =>
|
||||
val takeBranch = regs.compare(op.rs1, op.rs2, op.comp.run)
|
||||
if(takeBranch){
|
||||
val nextVM = copy(pc = addr)
|
||||
jump(nextVM, PcUpdateB(nextVM.pc))
|
||||
}
|
||||
else {
|
||||
step(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The weird :_* syntax is simply a way to pass a list to a varArgs function.
|
||||
*
|
||||
* To see why, consider def printMany(a: Any*); printMany(List(1,2,3))
|
||||
* It is now ambiguous if it should print "1, 2, 3" or List(1, 2, 3)
|
||||
*
|
||||
* This is disambiguated by appending :_*, thus
|
||||
* printMany(List(1,2,3):_*) == printMany(1, 2, 3)
|
||||
*/
|
||||
private def executeArith(op: Arith) = {
|
||||
val (regUpdate, nextRegs) = regs.arith(op.rd, op.rs1, op.rs2, op.op.run)
|
||||
val nextVM = this.copy(regs = nextRegs)
|
||||
Right(step(nextVM, regUpdate.toList:_*))
|
||||
}
|
||||
|
||||
|
||||
private def executeArithImm(op: ArithImm) = {
|
||||
val (regUpdate, nextRegs) = regs.arithImm(op.rd, op.rs1, op.imm, op.op.run)
|
||||
val nextVM = this.copy(regs = nextRegs)
|
||||
Right(step(nextVM, regUpdate.toList:_*))
|
||||
}
|
||||
|
||||
|
||||
private def executeLUI(op: LUI) = {
|
||||
val (regUpdate, nextRegs) = regs + (op.rd -> (op.imm.value << 12))
|
||||
val nextVM = this.copy(regs = nextRegs)
|
||||
Right(step(nextVM, regUpdate.toList:_*))
|
||||
}
|
||||
|
||||
|
||||
private def executeAUIPC(op: AUIPC) = {
|
||||
val (regUpdate, nextRegs) = regs + (op.rd -> (pc.value << 12))
|
||||
val nextVM = this.copy(regs = nextRegs)
|
||||
Right(step(nextVM, regUpdate.toList:_*))
|
||||
}
|
||||
|
||||
|
||||
|
||||
private def executeJALR(op: JALR) = getAddr(op.dst).map{ targetAddr =>
|
||||
val nextPc = Addr((regs.repr(op.rs1).value + targetAddr.value) & 0xFFFFFFFE)
|
||||
val (regUpdate, nextRegs) = regs + (op.rd -> (pc.step.value))
|
||||
val nextVM = this.copy(regs = nextRegs, pc = nextPc)
|
||||
jump(nextVM, (PcUpdateJALR(nextPc) :: regUpdate.toList):_*)
|
||||
}
|
||||
|
||||
|
||||
private def executeJAL(op: JAL) = getAddr(op.dst).map{ targetAddr =>
|
||||
val nextPc = targetAddr
|
||||
val (regUpdate, nextRegs) = regs + (op.rd -> (pc.step.value))
|
||||
val nextVM = this.copy(regs = nextRegs, pc = nextPc)
|
||||
jump(nextVM, (PcUpdateJAL(nextPc) :: regUpdate.toList):_*)
|
||||
}
|
||||
|
||||
|
||||
private def executeLW(op: LW) = dmem.read(Addr(regs.repr(op.rs1) + op.offset.value)).map{ case(event, result) =>
|
||||
val (regUpdate, nextRegs) = regs + (op.rd -> result)
|
||||
val nextVM = this.copy(regs = nextRegs)
|
||||
step(nextVM, (event :: regUpdate.toList):_*)
|
||||
}.left.map(x => Failed(x, pc))
|
||||
|
||||
private def executeSW(op: SW) = {
|
||||
val writeAddress = Addr(regs.repr(op.rs1) + op.offset.value)
|
||||
val writeData = regs.repr(op.rs2)
|
||||
dmem.write(writeAddress, writeData).map{ case(event, nextDmem) =>
|
||||
val nextVM = this.copy(dmem = nextDmem)
|
||||
step(nextVM, event)
|
||||
}
|
||||
}.left.map(x => Failed(x, pc))
|
||||
|
||||
private def step(nextVM: VM, event: ExecutionEvent*) =
|
||||
ExecutionTrace(nextVM.stepPC, ExecutionTraceEvent(pc, event:_*))
|
||||
|
||||
// Same as above, but no stepping
|
||||
private def jump(nextVM: VM, event: ExecutionEvent*) =
|
||||
ExecutionTrace(nextVM, ExecutionTraceEvent(pc, event:_*))
|
||||
|
||||
|
||||
private def stepPC: VM = copy(pc = this.pc.step)
|
||||
|
||||
private def getAddr(dst: Label): Either[Failed, Addr] =
|
||||
labelMap.lift(dst).toRight(Failed(s"Label $dst missing", pc))
|
||||
|
||||
private def getOp: Either[Failed, Op] =
|
||||
imem.lift(pc).toRight(Failed(s"Attempted to fetch instruction at illegal address ${pc.show}", pc))
|
||||
}
|
||||
|
||||
|
||||
object VM {
|
||||
|
||||
val init = VM(DMem.empty, Map[Addr, Op](), Regs.empty, Addr(0), Map[Label, Addr]())
|
||||
|
||||
def apply(settings: List[TestSetting], imem: Map[Addr, Op], labelMap: Map[Label, Addr]): VM = {
|
||||
val (dmem, regs) = settings.foldLeft((DMem.empty, Regs.empty)){ case((dmem, regs), setting) => setting match {
|
||||
case setting: REGSET => (dmem, regs(setting))
|
||||
case setting: MEMSET => (dmem(setting), regs)
|
||||
}
|
||||
}
|
||||
VM(dmem, imem, regs, Addr(0), labelMap)
|
||||
}
|
||||
|
||||
def run(maxSteps: Int, vm: VM) = {
|
||||
def helper(state: ExecutionTrace[VM], step: Int): (Finished, ExecutionTrace[VM]) = {
|
||||
if(step > 0){
|
||||
val (log, vm) = state.run
|
||||
val next = vm.stepInstruction
|
||||
next match {
|
||||
case Left(stopped) => (stopped, state)
|
||||
case Right(trace) => helper((state >> trace), step - 1)
|
||||
}
|
||||
}
|
||||
else{
|
||||
(Timeout, state)
|
||||
}
|
||||
}
|
||||
helper(ExecutionTrace(vm), maxSteps)
|
||||
}
|
||||
}
|
269
src/test/scala/RISCV/assembler.scala
Normal file
269
src/test/scala/RISCV/assembler.scala
Normal file
|
@ -0,0 +1,269 @@
|
|||
package FiveStage
|
||||
import cats.implicits._
|
||||
|
||||
import Data._
|
||||
import Ops._
|
||||
import fileUtils._
|
||||
import PrintUtils._
|
||||
|
||||
object assembler {
|
||||
|
||||
type InstructionFragment = Either[(String, Addr), Int]
|
||||
|
||||
/**
|
||||
* Will only be called by applyImmedate, thus error propagation is not needed
|
||||
* Kind of a kludge, but it works, don't touch!
|
||||
*/
|
||||
def setField(firstBit: Int, size: Int, field: Int): Int => Int = instruction => {
|
||||
val shiftedField = field << firstBit
|
||||
val mask = (1 << size) - 1
|
||||
val shiftedMask = (mask << firstBit)
|
||||
val masked = ((~instruction) | shiftedMask)
|
||||
val maskedInv = ~(masked)
|
||||
|
||||
val ret = (shiftedField & shiftedMask) | maskedInv
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
def getSubField(firstBit: Int, size: Int): Int => Int = word => {
|
||||
val bitsLeft = 32 - firstBit
|
||||
val bitsRight = 32 - size
|
||||
val leftShifted = word << bitsLeft
|
||||
val rightShifted = leftShifted >> bitsRight
|
||||
rightShifted
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Splits the immediate value into fields given by points.
|
||||
The order of points is important!
|
||||
points is of type idx, size
|
||||
*/
|
||||
def applyImmediate(immediateBits: Int, immediate: Int, points: List[(Int, Int)]): Int => Int = instruction => {
|
||||
|
||||
def go(instruction: Int, immediateIndex: Int, points: List[(Int,Int)]): Int = points match {
|
||||
case h :: t => {
|
||||
val (immFirstBit, size) = h
|
||||
val firstBit = (immFirstBit - size) + 1
|
||||
val immSubField = getSubField(immediateIndex, size)(immediate)
|
||||
val nextImmIndex = immediateIndex - size
|
||||
val nextInstruction = setField(firstBit, size, immSubField)(instruction)
|
||||
go(nextInstruction, nextImmIndex, points.tail)
|
||||
}
|
||||
case _ => {
|
||||
instruction
|
||||
}
|
||||
}
|
||||
|
||||
go(instruction, immediateBits, points)
|
||||
}
|
||||
|
||||
|
||||
def applyImmediateU(immediate: Int, points: List[(Int, Int)], addr: Addr): Int => InstructionFragment = instruction => {
|
||||
def totalBits = points.foldLeft(0){ case(acc, (first, size)) => acc + size }
|
||||
totalBits.nBitsU.toRight("Negative number used as unsigned immediate", addr).flatMap { bits =>
|
||||
Either.cond(bits < totalBits, applyImmediate(totalBits, immediate, points)(instruction), ("Immediate unsigned too large", addr))
|
||||
}
|
||||
}
|
||||
|
||||
def applyImmediateS(immediate: Int, points: List[(Int, Int)], addr: Addr): Int => InstructionFragment = instruction => {
|
||||
def totalBits = points.foldLeft(0){ case(acc, (first, size)) => acc + size }
|
||||
if(totalBits < immediate.nBitsS) Left((s"Immediate signed too large. immedate: $immediate, immediate.nBitsS = ${immediate.nBitsS}, total bits: $totalBits", addr))
|
||||
else Right(applyImmediate(totalBits, immediate, points)(instruction))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used by JALR, LW and arithmetic immediate ops.
|
||||
* JALR is sort of the odd man out here as it should be unsigned.
|
||||
* This issue should not surface at the very limited address space
|
||||
* for your design. (I hope)
|
||||
*/
|
||||
def setItypeImmediate(immediate: Int, addr: Addr): Int => InstructionFragment = {
|
||||
val points = List((31, 12))
|
||||
val withField = applyImmediateS(immediate, points, addr)
|
||||
withField
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by SW
|
||||
*/
|
||||
def setStypeImmediate(immediate: Int, addr: Addr): Int => InstructionFragment = {
|
||||
val points = List((31, 7), (11, 5))
|
||||
val withField = applyImmediateS(immediate, points, addr)
|
||||
withField
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Branches. PC relative jump, thus signed
|
||||
* Last bit is not used, hence the shift
|
||||
*/
|
||||
def setBtypeImmediate(immediate: Int, addr: Addr): Int => InstructionFragment = {
|
||||
val points = List((31, 1), (7, 1), (30, 6), (11, 4))
|
||||
val withField = applyImmediateS((immediate >> 1), points, addr)
|
||||
withField
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by LUI and AUIPC. Unsigned
|
||||
*/
|
||||
def setUtypeImmediate(immediate: Int, addr: Addr): Int => InstructionFragment = {
|
||||
val points = List((31, 20))
|
||||
val withField = applyImmediateU(immediate, points, addr)
|
||||
withField
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by JAL. PC relative jump, thus signed
|
||||
* The last bit is not used, hence the shift
|
||||
*/
|
||||
def setJtypeImmediate(immediate: Int, addr: Addr): Int => InstructionFragment = {
|
||||
val points = List((31, 1), (19, 8), (20, 1), (30, 10))
|
||||
applyImmediateU((immediate >> 1), points, addr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently not used, thus we allow too larg shifts.
|
||||
*/
|
||||
def setShiftTypeImmediate(instruction: Int, immediate: Int): Int = {
|
||||
val points = List((24, 5))
|
||||
val withField = applyImmediate(5, immediate, points)(instruction)
|
||||
withField
|
||||
}
|
||||
|
||||
def setOpCode(opcode: Int): Int => Int = setField(0, 7, opcode)
|
||||
def setFunct7(funct7: Int): Int => Int = setField(25, 7, funct7)
|
||||
def setFunct3(funct3: Int): Int => Int = setField(12, 3, funct3)
|
||||
def setRs1(rs1: Int): Int => Int = setField(15, 5, rs1)
|
||||
def setRs2(rs2: Int): Int => Int = setField(20, 5, rs2)
|
||||
def setRd(rd: Int): Int => Int = setField(7, 5, rd)
|
||||
|
||||
|
||||
def setOpCode(op: Op): Int => Int = op match {
|
||||
case x: Branch => setOpCode("1100011".binary)
|
||||
case x: Arith => setOpCode("0110011".binary)
|
||||
case x: ArithImm => setOpCode("0010011".binary)
|
||||
case x: LW => setOpCode("0000011".binary)
|
||||
case x: SW => setOpCode("0100011".binary)
|
||||
case x: JALR => setOpCode("1100111".binary)
|
||||
case x: JAL => setOpCode("1101111".binary)
|
||||
case x: AUIPC => setOpCode("0110111".binary)
|
||||
case x: LUI => setOpCode("0010111".binary)
|
||||
case DONE => setOpCode("0010011".binary) // done is turned into a NOP in the assembler.
|
||||
}
|
||||
|
||||
def setComparisonFunct(cmp: Comparison): Int => Int = cmp match {
|
||||
case EQ => setFunct3("000".binary)
|
||||
case NE => setFunct3("001".binary)
|
||||
case GE => setFunct3("101".binary)
|
||||
case LT => setFunct3("100".binary)
|
||||
case GEU => setFunct3("111".binary)
|
||||
case LTU => setFunct3("110".binary)
|
||||
}
|
||||
|
||||
def setBranchDestination(labelMap: Map[Label, Addr], op: Branch, opAddr: Addr): Int => InstructionFragment = instruction => {
|
||||
labelMap.lift(op.dst).toRight((s"destination ${op.dst} not found", opAddr)).flatMap{ dstAddr =>
|
||||
setBtypeImmediate(dstAddr.value - opAddr.value, opAddr)(instruction)
|
||||
}
|
||||
}
|
||||
|
||||
def setArithFunct(op: ArithOp): Int => Int = op match {
|
||||
case ADD => setFunct7("0000000".binary) andThen setFunct3("000".binary)
|
||||
case SUB => setFunct7("0100000".binary) andThen setFunct3("000".binary)
|
||||
case SLL => setFunct7("0000000".binary) andThen setFunct3("001".binary)
|
||||
case SLT => setFunct7("0000000".binary) andThen setFunct3("010".binary)
|
||||
case SLTU => setFunct7("0000000".binary) andThen setFunct3("011".binary)
|
||||
case XOR => setFunct7("0000000".binary) andThen setFunct3("100".binary)
|
||||
case SRL => setFunct7("0000000".binary) andThen setFunct3("101".binary)
|
||||
case SRA => setFunct7("0100000".binary) andThen setFunct3("101".binary)
|
||||
case OR => setFunct7("0000000".binary) andThen setFunct3("110".binary)
|
||||
case AND => setFunct7("0000000".binary) andThen setFunct3("111".binary)
|
||||
}
|
||||
|
||||
|
||||
def assembleRegLayout(op: RegLayout): Int => Int = {
|
||||
|
||||
def assembleRType(op: RType): Int => Int =
|
||||
setRd(op.rd.value) andThen
|
||||
setRs1(op.rs1.value) andThen
|
||||
setRs2(op.rs2.value)
|
||||
|
||||
def assembleIType(op: IType): Int => Int =
|
||||
setRd(op.rd.value) andThen
|
||||
setRs1(op.rs1.value)
|
||||
|
||||
def assembleSType(op: SType): Int => Int = {
|
||||
// say("stype")
|
||||
instruction =>
|
||||
(setRs1(op.rs1.value) andThen
|
||||
setRs2(op.rs2.value))(instruction)
|
||||
}
|
||||
|
||||
def assembleUType(op: UType): Int => Int =
|
||||
setRd(op.rd.value)
|
||||
|
||||
op match {
|
||||
case op: RType => assembleRType(op)
|
||||
case op: IType => assembleIType(op)
|
||||
case op: SType => assembleSType(op)
|
||||
case op: UType => assembleUType(op)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def assembleImmediate(op: Op, addr: Addr, labelMap: Map[Label, Addr]): Int => Either[(String, Addr), Int] = op match {
|
||||
case DONE => instruction => Right(instruction)
|
||||
case op: Arith => instruction => Right(instruction)
|
||||
case op: ArithImm => setItypeImmediate(op.imm.value, addr)
|
||||
case op: Branch => setBranchDestination(labelMap, op, addr)
|
||||
case op: JALR => instruction => labelMap.lift(op.dst).toRight(s"label ${op.dst} not found", addr).flatMap(addr => setItypeImmediate(addr.value, addr)(instruction))
|
||||
case op: AUIPC => setUtypeImmediate(op.imm.value, addr)
|
||||
case op: LUI => setUtypeImmediate(op.imm.value, addr)
|
||||
case op: LW => setItypeImmediate(op.offset.value, addr)
|
||||
case op: SW => setStypeImmediate(op.offset.value, addr)
|
||||
case op: JAL => instruction => {
|
||||
val addressDistance = labelMap
|
||||
.lift(op.dst).toRight(s"label ${op.dst} not found", addr)
|
||||
.map(absoluteAddr => absoluteAddr - addr)
|
||||
|
||||
import PrintUtils._
|
||||
|
||||
addressDistance.flatMap(distance =>
|
||||
setJtypeImmediate(distance.value, addr)(instruction))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def assembleOp(
|
||||
op : Op with RegLayout,
|
||||
opAddr : Addr,
|
||||
labelMap : Map[Label, Addr]): Either[(String, Addr), Int] = {
|
||||
|
||||
val layout = assembleRegLayout(op)
|
||||
val immediate = assembleImmediate(op, opAddr, labelMap)
|
||||
val opcode = setOpCode(op)
|
||||
|
||||
val extras: Int => Int = (instruction: Int) => op match {
|
||||
case op: Branch => setComparisonFunct(op.comp)(instruction)
|
||||
case op: ArithImm => setArithFunct(op.op)(instruction)
|
||||
case op: Arith => setArithFunct(op.op)(instruction)
|
||||
case op: JALR => setFunct3("000".binary)(instruction)
|
||||
case op: LW => setFunct3("010".binary)(instruction)
|
||||
case op: SW => setFunct3("010".binary)(instruction)
|
||||
case DONE => (setFunct3("000".binary) andThen setFunct7("0000000".binary))(instruction)
|
||||
case _ => instruction
|
||||
}
|
||||
|
||||
val withOp = opcode(0)
|
||||
val withLayout = layout(withOp)
|
||||
val withImmediates = immediate(withLayout)
|
||||
val withExtras = withImmediates.map(extras)
|
||||
|
||||
|
||||
val finalOp = (opcode andThen layout andThen extras andThen immediate)(0)
|
||||
|
||||
finalOp
|
||||
}
|
||||
}
|
178
src/test/scala/RISCV/deleteMe.scala
Normal file
178
src/test/scala/RISCV/deleteMe.scala
Normal file
|
@ -0,0 +1,178 @@
|
|||
package FiveStage
|
||||
import cats.data.Writer
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
|
||||
object DTree {
|
||||
|
||||
// opaques WHEN
|
||||
type Feature = String
|
||||
type Value = String
|
||||
type Cls = String
|
||||
|
||||
case class TrainingData(values: Map[Feature, Value], cls: Cls)
|
||||
|
||||
type TestData = Map[Feature, Value]
|
||||
|
||||
// Base
|
||||
// def predict(data: TestData): Cls = "Died"
|
||||
|
||||
// Gendered
|
||||
def predictStatic(data: TestData): Cls =
|
||||
if(data("gender") == "female")
|
||||
"survived"
|
||||
else "died"
|
||||
|
||||
|
||||
sealed trait Tree
|
||||
case class Node(feature: Feature, children: Map[Value, Tree]) extends Tree
|
||||
case class Leaf(cls: Cls) extends Tree
|
||||
|
||||
val genderBased: Tree = Node(
|
||||
"gender", Map(
|
||||
"female" -> Leaf("survived"),
|
||||
"male" -> Leaf("died"),
|
||||
))
|
||||
|
||||
|
||||
def predict(data: TestData)(tree: Tree): Cls =
|
||||
tree match {
|
||||
case Leaf(cls) => cls
|
||||
case Node(feature, children) => predict(data)(children(data(feature)))
|
||||
}
|
||||
|
||||
val data = Map("gender" -> "female", "family size" -> "0", "ticket" -> "1")
|
||||
|
||||
predict(data)(genderBased) // true
|
||||
|
||||
|
||||
def entropy(classes: List[Cls]): Double = {
|
||||
val total = classes.size
|
||||
classes.groupBy(identity)
|
||||
.mapValues { group =>
|
||||
val prop = group.size / total
|
||||
prop * math.log(1.0 / prop)
|
||||
}.values.sum
|
||||
}
|
||||
|
||||
def bucketedEntropy(data: List[TrainingData], feature: Feature): Double = {
|
||||
val total = data.size
|
||||
val bucketed = data.groupBy(_.values(feature))
|
||||
.mapValues(_.map(_.cls))
|
||||
.toMap
|
||||
bucketed.values.map { classes =>
|
||||
val prop = classes.size / total
|
||||
prop * entropy(classes)
|
||||
}.sum
|
||||
}
|
||||
|
||||
def best(data: List[TrainingData], features: Set[Feature]): Feature = features.minBy(bucketedEntropy(data, _))
|
||||
|
||||
def mostCommonCls(data: List[TrainingData]): Cls = ???
|
||||
|
||||
def build(data: List[TrainingData], features: Set[Feature]): Tree = {
|
||||
if(features.nonEmpty) {
|
||||
val feature = best(data, features)
|
||||
val buckets = data.groupBy(_.values(feature))
|
||||
Node(feature, buckets.mapValues(build(_, features - feature)))
|
||||
} else {
|
||||
Leaf(mostCommonCls(data))
|
||||
}
|
||||
}
|
||||
|
||||
def withHKT {
|
||||
|
||||
sealed trait Tree[A]
|
||||
case class Node[A](feature: Feature, children: Map[Value, A]) extends Tree[A]
|
||||
case class Leaf[A](cls: Cls) extends Tree[A]
|
||||
|
||||
// import matryoshka._
|
||||
// import matryoshka.data.Fix
|
||||
// import matryoshka.implicits._
|
||||
|
||||
case class Fix[F[_]](unfix: F[Fix[F]])
|
||||
case class Cofree[F[_], A](head: A, tail: F[Cofree[F, A]]){
|
||||
def counit: A = head
|
||||
def map[B](f: A => B)(implicit ev: Functor[F]): Cofree[F, B] = Cofree(f(head), tail.map(_.map(f)))
|
||||
|
||||
/**
|
||||
* Coflatmap alters the value of the node based on its context, then recursively
|
||||
* alters its tail independently (which makes sense as it's the only thing Cofree[F, A] => B can do.
|
||||
*/
|
||||
def coflatMap[B](fa: Cofree[F, A] => B)(implicit ev: Functor[F]): Cofree[F, B] = {
|
||||
val b = fa(this)
|
||||
val fb = tail.map(_.coflatMap(fa))
|
||||
Cofree(b, fb)
|
||||
}
|
||||
}
|
||||
|
||||
implicit val treeFunctor: Functor[Tree] = new Functor[Tree] {
|
||||
def map[A, B](fa: Tree[A])(f: A => B): Tree[B] = fa match {
|
||||
case Node(name, children) => Node(name, children.mapValues(f))
|
||||
case Leaf(cls) => Leaf(cls)
|
||||
}
|
||||
}
|
||||
|
||||
val genderBased: Fix[Tree] =
|
||||
Fix(Node(
|
||||
"gender",
|
||||
Map(
|
||||
"female" -> Fix(Leaf[Fix[Tree]]("survived")),
|
||||
"male" -> Fix(Leaf[Fix[Tree]]("died"))
|
||||
)))
|
||||
|
||||
def build: ((List[TrainingData], Set[Feature])) => Tree[(List[TrainingData], Set[Feature])] = {
|
||||
case (data, features) =>
|
||||
if(features.nonEmpty) {
|
||||
val feature = best(data, features)
|
||||
val buckets = data.groupBy(_.values(feature))
|
||||
val next = buckets.mapValues { subset => (subset, features - feature) }
|
||||
Node(feature, next)
|
||||
} else {
|
||||
Leaf(mostCommonCls(data))
|
||||
}
|
||||
}
|
||||
|
||||
def explore(testData: TestData): Fix[Tree] => Cls Either Fix[Tree] =
|
||||
fix => fix.unfix match {
|
||||
case Leaf(cls) => Left(cls)
|
||||
case Node(feature, children) => Right(children.get(testData(feature)).get)
|
||||
}
|
||||
|
||||
// Anamorphism: Generalized unfold, builds structures
|
||||
def ana[F[_]: Functor, A](f: A => F[A])(a: A): Fix[F] =
|
||||
Fix( (f(a)).map(ana(f)) )
|
||||
|
||||
// Catamorphism: Generalized fold, tears structures down.
|
||||
def cata[F[_]: Functor, A](fa: F[A] => A)(f: Fix[F]): A = {
|
||||
fa(f.unfix.map(cata(fa)))
|
||||
}
|
||||
|
||||
// def hyloSimple[F[_] : Functor, A, B](f: F[B] => B)(g: A => F[A]): A => B
|
||||
def hyloSimple[F[_]: Functor, A, B](f: F[B] => B)(g: A => F[A])(a: A): B =
|
||||
cata(f)(ana(g)(a))
|
||||
|
||||
// A more powerful cata
|
||||
def para[F[_]: Functor, A](f: F[(Fix[F], A)] => A)(fa: Fix[F]): A =
|
||||
f(fa.unfix.map(x => (x, para(f)(x))))
|
||||
|
||||
// A more powerful ana
|
||||
def apo[F[_]: Functor, A](f: A => F[Either[Fix[F], A]])(a: A): Fix[F] = {
|
||||
Fix(f(a).map{
|
||||
case Right(a) => apo(f)(a)
|
||||
case Left(fix) => fix
|
||||
})
|
||||
}
|
||||
|
||||
// When we have cofree
|
||||
def histo[F[_]: Functor, A](f: F[Cofree[F, A]] => A)(fix: Fix[F]): A = {
|
||||
def toCofree(fix: Fix[F]): Cofree[F, A] =
|
||||
Cofree(histo(f)(fix), fix.unfix.map(toCofree))
|
||||
|
||||
f(fix.unfix.map(toCofree))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
164
src/test/scala/RISCV/deleteMe2.scala
Normal file
164
src/test/scala/RISCV/deleteMe2.scala
Normal file
|
@ -0,0 +1,164 @@
|
|||
package FiveStage
|
||||
import cats.data.Writer
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
|
||||
import fileUtils.say
|
||||
|
||||
object DeletDis {
|
||||
|
||||
def delet = {
|
||||
|
||||
case class Fix[F[_]](unfix: F[Fix[F]])
|
||||
case class Cofree[F[_], A](head: A, tail: F[Cofree[F, A]]){
|
||||
def counit: A = head
|
||||
def map[B](f: A => B)(implicit ev: Functor[F]): Cofree[F, B] = Cofree(f(head), tail.map(_.map(f)))
|
||||
|
||||
/**
|
||||
* Coflatmap alters the value of the node based on its context, then recursively
|
||||
* alters its tail independently (which makes sense as it's the only thing Cofree[F, A] => B can do.
|
||||
*/
|
||||
def coflatMap[B](fa: Cofree[F, A] => B)(implicit ev: Functor[F]): Cofree[F, B] = {
|
||||
val b = fa(this)
|
||||
val fb = tail.map(_.coflatMap(fa))
|
||||
Cofree(b, fb)
|
||||
}
|
||||
}
|
||||
|
||||
// Anamorphism: Generalized unfold, builds structures
|
||||
def ana[F[_]: Functor, A](f: A => F[A])(a: A): Fix[F] =
|
||||
Fix( (f(a)).map(ana(f)) )
|
||||
|
||||
// Catamorphism: Generalized fold, tears structures down.
|
||||
def cata[F[_]: Functor, A](fa: F[A] => A)(f: Fix[F]): A = {
|
||||
fa(f.unfix.map(cata(fa)))
|
||||
}
|
||||
|
||||
// def hyloSimple[F[_] : Functor, A, B](f: F[B] => B)(g: A => F[A]): A => B
|
||||
def hylo[F[_]: Functor, A, B](f: F[B] => B)(g: A => F[A])(a: A): B =
|
||||
cata(f)(ana(g)(a))
|
||||
|
||||
// A more powerful cata
|
||||
def para[F[_]: Functor, A](f: F[(Fix[F], A)] => A)(fa: Fix[F]): A =
|
||||
f(fa.unfix.map(x => (x, para(f)(x))))
|
||||
|
||||
// A more powerful ana
|
||||
def apo[F[_]: Functor, A](f: A => F[Either[Fix[F], A]])(a: A): Fix[F] = {
|
||||
Fix(f(a).map{
|
||||
case Right(a) => apo(f)(a)
|
||||
case Left(fix) => fix
|
||||
})
|
||||
}
|
||||
|
||||
// When we have cofree
|
||||
def histo[F[_]: Functor, A](f: F[Cofree[F, A]] => A)(fix: Fix[F]): A = {
|
||||
def toCofree(fix: Fix[F]): Cofree[F, A] =
|
||||
Cofree(histo(f)(fix), fix.unfix.map(toCofree))
|
||||
|
||||
f(fix.unfix.map(toCofree))
|
||||
}
|
||||
|
||||
sealed trait StackR
|
||||
final case class DoneR(result: Int = 1) extends StackR
|
||||
final case class MoreR(stack: StackR, next: Int) extends StackR
|
||||
|
||||
def unfoldStackR(n: Int): StackR =
|
||||
if(n > 0) MoreR(unfoldStackR(n-1), n) else DoneR()
|
||||
|
||||
say(unfoldStackR(5))
|
||||
|
||||
sealed trait Stack[A]
|
||||
final case class Done[A](result: Int) extends Stack[A]
|
||||
final case class More[A](a: A, next: Int) extends Stack[A]
|
||||
|
||||
object Stack {
|
||||
implicit val stackFunctor: Functor[Stack] = new Functor[Stack] {
|
||||
def map[A, B](fa: Stack[A])(f: A => B): Stack[B] = fa match {
|
||||
case Done(result) => Done(result)
|
||||
case More(a, next) => More(f(a), next)
|
||||
}
|
||||
}
|
||||
|
||||
def done[A](result: Int = 1): Stack[A] = Done(result)
|
||||
def more[A](a: A, next: Int): Stack[A] = More(a, next)
|
||||
}
|
||||
|
||||
import Stack._
|
||||
|
||||
val stackCoalgebra: Int => Stack[Int] =
|
||||
n => if(n > 0) more(n - 1, n) else done()
|
||||
|
||||
say(ana(stackCoalgebra)(5))
|
||||
|
||||
val stackAlgebra: Stack[Int] => Int = {
|
||||
case Done(result) => result
|
||||
case More(acc, next) => acc * next
|
||||
}
|
||||
|
||||
say(cata(stackAlgebra)(ana(stackCoalgebra)(5)))
|
||||
say(hylo(stackAlgebra)(stackCoalgebra)(5))
|
||||
|
||||
|
||||
sealed trait Nat[A]
|
||||
final case class Zero[A]() extends Nat[A]
|
||||
final case class Succ[A](prev: A) extends Nat[A]
|
||||
|
||||
object Nat {
|
||||
implicit val natFunctor: Functor[Nat] = new Functor[Nat] {
|
||||
override def map[A, B](na: Nat[A])(f: A => B): Nat[B] =
|
||||
na match {
|
||||
case Zero() => Zero()
|
||||
case Succ(a) => Succ(f(a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val natAlgebra: Nat[Int] => Int = {
|
||||
case Zero() => 1
|
||||
case Succ(n) => {
|
||||
say(s"nat alg succ $n")
|
||||
n + 1
|
||||
}
|
||||
}
|
||||
|
||||
val natAlgebraS: Nat[String] => String = {
|
||||
case Zero() => "N"
|
||||
case Succ(n) => n match {
|
||||
case "N" => "NI"
|
||||
case "NI" => "NIG"
|
||||
case "NIG" => "NIGG"
|
||||
case "NIGG" => "NIGGE"
|
||||
case "NIGGE" => "NIGGER :-"
|
||||
case s => s + "D"
|
||||
}
|
||||
}
|
||||
|
||||
val natCoalgebra: Int => Nat[Int] =
|
||||
n => if (n == 0) Zero() else Succ(n - 1)
|
||||
|
||||
val b = ana(natCoalgebra)(9)
|
||||
val c = cata(natAlgebraS)(b)
|
||||
say(c)
|
||||
|
||||
|
||||
val natAlgebraPara: Nat[(Fix[Nat], Int)] => Int = {
|
||||
case Zero() => 1
|
||||
case Succ((fix, acc)) => {
|
||||
say(s"nat para alg succ $fix, $acc")
|
||||
cata(natAlgebra)(fix) * acc
|
||||
}
|
||||
}
|
||||
|
||||
val build = ana(natCoalgebra)(_)
|
||||
say("built")
|
||||
val tear = para(natAlgebraPara)(_)
|
||||
say(tear(build(5)))
|
||||
// say(ana(natCoalgebra)(5))
|
||||
|
||||
val lastThreeSteps: Fix[Stack] = Fix(More(Fix(More(Fix(More(Fix(Done(1)),1)),2)),3))
|
||||
|
||||
val stackCoalgebraApo: Int => Stack[Either[Fix[Stack], Int]] =
|
||||
n => if(n > 3) more(n - 1, n).map(_.asRight) else lastThreeSteps.unfix.map(_.asLeft)
|
||||
}
|
||||
}
|
352
src/test/scala/RISCV/printUtils.scala
Normal file
352
src/test/scala/RISCV/printUtils.scala
Normal file
|
@ -0,0 +1,352 @@
|
|||
package FiveStage
|
||||
import cats.data.Writer
|
||||
import cats._
|
||||
import cats.data.{ Op => _ }
|
||||
import cats.implicits._
|
||||
|
||||
import fansi._
|
||||
|
||||
import Ops._
|
||||
import Data._
|
||||
import VM._
|
||||
import fileUtils._
|
||||
|
||||
object PrintUtils {
|
||||
|
||||
// Minimal typeclass def for being fancy
|
||||
trait Fancy[-A] { def s(a: A): fansi.Str }
|
||||
implicit class FancyOps[-A](a: A)(implicit ev: Fancy[A]){ def show: fansi.Str = ev.s(a) }
|
||||
implicit val DoubleFancy = new Fancy[fansi.Str]{ def s(a: fansi.Str) = a }
|
||||
implicit val FancyMonoid = new Monoid[fansi.Str] {
|
||||
def empty: fansi.Str = fansi.Str("")
|
||||
def combine(self: fansi.Str, that: fansi.Str): fansi.Str = self ++ that
|
||||
}
|
||||
|
||||
implicit class ExtraFancy(a: fansi.Str) {
|
||||
def leftPad(length: Int): fansi.Str = Str((0 until length).map(_ => ' ').mkString) ++ a
|
||||
def replace(from: Char, to: Char): fansi.Str = fansi.Str(a.toString.replace(from, to))
|
||||
def padTo(length: Int, pad: Char = ' '): fansi.Str = a ++ (0 until length - a.length).map(_ => pad).mkString
|
||||
def trim: fansi.Str = fansi.Str(a.toString.trim)
|
||||
}
|
||||
|
||||
implicit val RegFancy = new Fancy[Reg] { def s(r: Reg) = fansi.Str(regNames.canonicalName.lift(r.value).getOrElse("ERROR")) }
|
||||
implicit val ImmFancy = new Fancy[Imm] { def s(r: Imm) = Str(r.value.hs) }
|
||||
implicit val AddrFancy = new Fancy[Addr]{ def s(r: Addr) = Str(r.value.hs) }
|
||||
|
||||
implicit val ExecutionEventFancy = new Fancy[ExecutionEvent]{
|
||||
def s(a: ExecutionEvent): fansi.Str = a match {
|
||||
case RegUpdate(reg, word) => Str(s"R(${reg.show}) <- 0x${word.hs}")
|
||||
case MemWrite(addr, word) => fansi.Color.Blue(s"M[${addr.show}] <- 0x${word.hs}")
|
||||
case MemRead(addr, word) => fansi.Color.Red(f"M[${addr.show}] -> 0x${word.hs}")
|
||||
|
||||
// addr is the target address
|
||||
case PcUpdateJALR(addr) => fansi.Color.Green(s"PC updated to ${addr.show} via JALR")
|
||||
case PcUpdateJAL(addr) => fansi.Color.Magenta(s"PC updated to ${addr.show} via JAL")
|
||||
case PcUpdateB(addr) => fansi.Color.Yellow(s"PC updated to ${addr.show} via Branch")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val ExecutionTraceEventFancy = new Fancy[ExecutionTraceEvent]{
|
||||
def s(a: ExecutionTraceEvent): fansi.Str = a.event.toList match {
|
||||
case (h: ExecutionEvent) :: (t: RegUpdate) :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemWrite) :: t :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemRead) :: t :: Nil => t.show.padTo(25) ++ h.show
|
||||
case (h: MemWrite) :: Nil => h.show.leftPad(25)
|
||||
case (h: MemRead) :: Nil => h.show.leftPad(25)
|
||||
case h :: t :: Nil => h.show ++ t.show
|
||||
case (h: RegUpdate) :: Nil => h.show
|
||||
case h :: Nil => h.show.leftPad(25)
|
||||
case _ => Str("")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val ChiselEventFancy = new Fancy[ChiselEvent]{
|
||||
def s(e: ChiselEvent) = e match {
|
||||
case ChiselRegEvent(addr, reg, word) => fansi.Str(s"R(${reg.show}) <- ${word.hs}")
|
||||
case ChiselMemWriteEvent(addr, memAddr, word) => fansi.Str(s"M[${memAddr.show}] <- ${word.hs}")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val RegsFancy = new Fancy[Regs]{
|
||||
def s(e: Regs) = {
|
||||
(0 to 9).map{ idx =>
|
||||
val cols = List.range(idx, 32, 10)
|
||||
cols.map{ reg =>
|
||||
(fansi.Color.Yellow(Reg(reg).show.padTo(5)) ++ Str(e.repr.lift(Reg(reg)).getOrElse(0).hs)).padTo(16)
|
||||
}.showN("| ")
|
||||
}.toList.showN("\n", "\n", "\n")
|
||||
}
|
||||
}
|
||||
|
||||
val UNKNOWN = "UNKNOWN"
|
||||
|
||||
def printInstruction(op: Ops.Op, labelMap: Map[Label, Addr]): fansi.Str = op match {
|
||||
case op: Branch => fansi.Color.Red(s"B${op.comp}\t${op.rs1.show}, ${op.rs2.show}, ${op.dst.show}\t[${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: Arith => s"${op.op}\t${op.rd.show}, ${op.rs1.show}, ${op.rs2.show}"
|
||||
case op: ArithImm => s"${op.op}I\t${op.rd.show}, ${op.rs1.show}, ${op.imm.show}"
|
||||
case op: JALR => fansi.Color.Green(s"JALR\t${op.rd.show}, ${op.rs1.show}, ${op.dst.show}\t[${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: JAL => fansi.Color.Magenta(s"JAL\t${op.rd.show}, ${op.dst.show} [${labelMap.lift(op.dst).getOrElse(UNKNOWN)}]")
|
||||
case op: LW => s"LW\t${op.rd.show}, ${op.offset.show}(${op.rs1.show})"
|
||||
case op: SW => s"SW\t${op.rs2.show}, ${op.offset.show}(${op.rs1.show})"
|
||||
case op: LUI => s"LUI\t${op.rd.show}, ${op.imm.show}"
|
||||
case op: AUIPC => s"AUIPC\t${op.rd.show}, ${op.imm.show}"
|
||||
case DONE => s"DONE"
|
||||
}
|
||||
|
||||
|
||||
implicit class IntPrinters(i: Int) {
|
||||
def hs: String = if(i > 65535) f"0x$i%08X" else f"0x$i%04X"
|
||||
def binary: String = String.format("%" + 32 + "s", i.toBinaryString)
|
||||
.replace(' ', '0').grouped(4)
|
||||
.map(x => x + " ").mkString
|
||||
}
|
||||
|
||||
|
||||
|
||||
def printProgram(vm: VM): String = {
|
||||
val withLabels: List[Either[(Addr, Op), (Label, Addr)]] = (vm.imem.toList.map(Left(_)) ::: vm.labelMap.toList.map(Right(_)))
|
||||
.sortBy { case Left(x) => x._1.value.toDouble + 0.1; case Right(x) => x._2.value.toDouble }
|
||||
|
||||
withLabels.map{
|
||||
case Left((addr, op)) => s"$addr:\t\t${printInstruction(op, vm.labelMap)}"
|
||||
case Right((label, addr)) => s"$addr:\t$label:"
|
||||
}.mkString("\n","\n","\n")
|
||||
}
|
||||
|
||||
|
||||
def printProgram(p: Program): String = printProgram(p.vm)
|
||||
|
||||
|
||||
def printBinary(bin: Map[Addr, Int]): String = {
|
||||
bin.toList.sortBy(_._1.value).map{ case(addr, op) => s"$addr: ${op.hs}" }.mkString("\n","\n","\n")
|
||||
}
|
||||
|
||||
|
||||
def printChiselLogEntry(event: (Addr, List[ChiselEvent])): fansi.Str = {
|
||||
val (addr, log) = event
|
||||
val events = log match {
|
||||
case (h: ChiselRegEvent) :: (t: ChiselMemWriteEvent) :: Nil => h.show.padTo(25, ' ') ++ t.show
|
||||
case (h: ChiselMemWriteEvent) :: Nil => h.show.leftPad(25)
|
||||
case (h: ChiselRegEvent) :: Nil => h.show
|
||||
case _ => fansi.Str("")
|
||||
}
|
||||
events
|
||||
}
|
||||
|
||||
|
||||
def printVMtrace(trace: List[ExecutionTraceEvent], program: Program): String = {
|
||||
val blocks: List[List[(ExecutionTraceEvent, Int)]] = LogParser.splitToBlocks(trace).zipWithIndexNested
|
||||
blocks.map{ block =>
|
||||
val name = LogParser.guessBlockName(block.map(_._1.pc), program.labelMapReverse)
|
||||
val blockString = block.map{ case(event, idx) =>
|
||||
Str(s"Step: $idx,").padTo(12) ++
|
||||
Str("PC: ") ++
|
||||
event.pc.show ++
|
||||
Str("\t") ++
|
||||
event.show.padTo(70) ++
|
||||
printSourceLine(event.pc, program)
|
||||
}
|
||||
fansi.Color.Yellow(name) ++ Str("\n") ++ blockString.showN("\n")
|
||||
}.mkStringN
|
||||
}
|
||||
|
||||
def printSourceLine(addr: Addr, program: Program): fansi.Str = program.sourceMap.lift(addr).map(fansi.Str(_)).getOrElse(fansi.Str("???"))
|
||||
|
||||
def printMergedTraces(
|
||||
mt: (List[ExecutionTraceEvent], List[CircuitTrace]),
|
||||
program: Program
|
||||
): List[String] = {
|
||||
|
||||
|
||||
/**
|
||||
* For branch predicting processors we may end up with a spurious block causing a transient
|
||||
* desynchronization. Therefore it is necessary to have a certain tolerance before calling a divergence.
|
||||
*/
|
||||
// TODO: Implement a synchronization test to pinpoint where a desync happens
|
||||
// This is not straight forward in the case of processors which perform branch prediction.
|
||||
def isSynchronized(desyncs: Int, traces: (List[ExecutionTraceEvent], List[CircuitTrace]) ): Boolean = ???
|
||||
|
||||
|
||||
def helper(mt: (List[ExecutionTraceEvent], List[CircuitTrace])): List[List[fansi.Str]] = mt match {
|
||||
/**
|
||||
* VM trace and execution log synchronized, step both
|
||||
*/
|
||||
case (hVM :: tVM, hC :: tC) if (hVM.pc == hC._1) => {
|
||||
val address = hVM.pc
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = hVM.show
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(address.show, chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((tVM, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* VM trace and execution log unsynchronized, step only chisel trace
|
||||
* Helper is called with (hVM :: tVM) to only step the chisel log.
|
||||
*/
|
||||
case (hVM :: tVM, hC :: tC) => {
|
||||
val address = hC._1
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = Str("")
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(address.show, chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((hVM :: tVM, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Block contains no more instructions performed by the VM. This
|
||||
* happens naturally since 5-stage pipelines will fetch spurious instructions.
|
||||
*/
|
||||
case (Nil, hC :: tC) => {
|
||||
val address = hC._1
|
||||
val chiselLogEntry = printChiselLogEntry(hC)
|
||||
val vmEntry = fansi.Str("")
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(fansi.Color.LightBlue(address.show), chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((Nil, tC))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Block contains no more instructions performed by the circuit.
|
||||
* This happens when the circuit takes a jump the VM does not.
|
||||
* This is an error *unless* the circuit has a branch predictor.
|
||||
*
|
||||
* If you want to you can splice the logs when this happens, but it's typically easy to spot.
|
||||
*/
|
||||
case (hVM :: tVM, Nil) => {
|
||||
val address = hVM.pc
|
||||
val chiselLogEntry = fansi.Str("")
|
||||
val vmEntry = hVM.show
|
||||
val sourceLine = printSourceLine(address, program)
|
||||
|
||||
val line = List(fansi.Color.LightRed(address.show), chiselLogEntry, vmEntry, sourceLine)
|
||||
|
||||
line :: helper((tVM, Nil))
|
||||
}
|
||||
|
||||
case (Nil, Nil) => Nil
|
||||
}
|
||||
|
||||
|
||||
def format(triplet: List[fansi.Str]): String = {
|
||||
// Ahh, the GNU toolchain and its tabs
|
||||
val annoyingTabCharacter = ' '
|
||||
triplet match {
|
||||
case address :: ch :: vm :: source :: Nil => {
|
||||
val vmLine: fansi.Str = vm.padTo(60, ' ')
|
||||
val chiselLine: fansi.Str = ch.padTo(50, ' ')
|
||||
val sourceLines: fansi.Str = source
|
||||
((address ++ Str(":")).padTo(14, ' ') ++ vmLine ++ fansi.Str("| ") ++ chiselLine ++ Str("| ") ++ sourceLines).toString
|
||||
}
|
||||
case _ => ""
|
||||
}
|
||||
}
|
||||
|
||||
val blockName = LogParser.guessBlockName(mt._1.map(_.pc), program.labelMapReverse)
|
||||
|
||||
(fansi.Color.Yellow(s"$blockName").padTo(74, ' ').toString ++ "|".padTo(55, ' ') ++ "|") :: helper(mt).map(format)
|
||||
}
|
||||
|
||||
|
||||
def printChiselError(e: Throwable): String = {
|
||||
val rawError = """
|
||||
|This typically occurs when you forget to wrap a module
|
||||
|e.g
|
||||
|
|
||||
|class MyBundle extends Bundle {
|
||||
| val signal = UInt(32.W)
|
||||
|}
|
||||
|val mySignal = new MyBundle
|
||||
| ^^^^ Wrong!
|
||||
|should be
|
||||
|val mySignal = Wire(new MyBundle)
|
||||
""".stripMargin
|
||||
|
||||
def getFiveStageStacktrace(strace: Array[StackTraceElement]): String =
|
||||
strace.map(_.toString).filter(_.contains("FiveStage")).toList.take(5).mkStringN
|
||||
|
||||
e match {
|
||||
case e: firrtl.passes.CheckInitialization.RefNotInitializedException => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design has unconnected wires!"
|
||||
|error:\n"
|
||||
|${e.getMessage}
|
||||
|
|
||||
|
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: chisel3.core.Binding.ExpectedHardwareException => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design is using raw chisel types!
|
||||
|error:\n
|
||||
|${e.getMessage}
|
||||
|
|
||||
|$rawError
|
||||
|The error should be here somewhere:
|
||||
|${getFiveStageStacktrace(e.getStackTrace)}
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: firrtl.passes.PassExceptions => {
|
||||
val rawErrorMsg = if(e.getMessage.contains("raw"))
|
||||
s"One of the errors was use of raw chisel types. $rawError"
|
||||
else
|
||||
""
|
||||
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Your design has multiple errors!
|
||||
|error:
|
||||
|${e.getMessage}
|
||||
|$rawErrorMsg
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
case e: Exception => {
|
||||
s"""
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|Unexpected Chisel tester error (could be a chisel error I havent accounted for, or a bug in the tester like index out of bounds.)
|
||||
|error:
|
||||
|${e.getMessage}
|
||||
|reduced stacktrace:
|
||||
|${getFiveStageStacktrace(e.getStackTrace)}
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
|##########################################################
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def printLogSideBySide(trace: List[ExecutionTraceEvent], chiselTrace: List[CircuitTrace], program: Program): String = {
|
||||
import LogParser._
|
||||
val traces = mergeTraces(trace, chiselTrace).map(x => printMergedTraces((x), program))
|
||||
traces.map(_.mkString("\n")).mkString("\n", "\n--------------------------------------------------------------------------+------------------------------------------------------+-------------------------------\n", "\n")
|
||||
}
|
||||
}
|
114
src/test/scala/RISCV/regNames.scala
Normal file
114
src/test/scala/RISCV/regNames.scala
Normal file
|
@ -0,0 +1,114 @@
|
|||
package FiveStage
|
||||
object regNames {
|
||||
val x0 = 0
|
||||
val x1 = 1
|
||||
val x2 = 2
|
||||
val x3 = 3
|
||||
val x4 = 4
|
||||
val x5 = 5
|
||||
val x6 = 6
|
||||
val x7 = 7
|
||||
val x8 = 8
|
||||
val x9 = 9
|
||||
|
||||
val x10 = 10
|
||||
val x11 = 11
|
||||
val x12 = 12
|
||||
val x13 = 13
|
||||
val x14 = 14
|
||||
val x15 = 15
|
||||
val x16 = 16
|
||||
val x17 = 17
|
||||
val x18 = 18
|
||||
val x19 = 19
|
||||
|
||||
val x20 = 20
|
||||
val x21 = 21
|
||||
val x22 = 22
|
||||
val x23 = 23
|
||||
val x24 = 24
|
||||
val x25 = 25
|
||||
val x26 = 26
|
||||
val x27 = 27
|
||||
val x28 = 28
|
||||
val x29 = 29
|
||||
|
||||
val x30 = 30
|
||||
val x31 = 31
|
||||
|
||||
|
||||
val zero = 0
|
||||
val ra = 1
|
||||
val sp = 2
|
||||
val gp = 3
|
||||
val tp = 4
|
||||
val t0 = 5
|
||||
val t1 = 6
|
||||
val t2 = 7
|
||||
val s0 = 8
|
||||
val fp = 8
|
||||
val s1 = 9
|
||||
|
||||
val a0 = 10
|
||||
val a1 = 11
|
||||
val a2 = 12
|
||||
val a3 = 13
|
||||
val a4 = 14
|
||||
val a5 = 15
|
||||
val a6 = 16
|
||||
val a7 = 17
|
||||
val s2 = 18
|
||||
val s3 = 19
|
||||
|
||||
val s4 = 20
|
||||
val s5 = 21
|
||||
val s6 = 22
|
||||
val s7 = 23
|
||||
val s8 = 24
|
||||
val s9 = 25
|
||||
val s10 = 26
|
||||
val s11 = 27
|
||||
val t3 = 28
|
||||
val t4 = 29
|
||||
|
||||
val t5 = 30
|
||||
val t6 = 31
|
||||
|
||||
val canonicalName = Map(
|
||||
0 -> "zero",
|
||||
1 -> "ra",
|
||||
2 -> "sp",
|
||||
3 -> "gp",
|
||||
4 -> "tp",
|
||||
5 -> "t0",
|
||||
6 -> "t1",
|
||||
7 -> "t2",
|
||||
8 -> "s0",
|
||||
8 -> "fp",
|
||||
9 -> "s1",
|
||||
10 -> "a0",
|
||||
11 -> "a1",
|
||||
12 -> "a2",
|
||||
13 -> "a3",
|
||||
14 -> "a4",
|
||||
15 -> "a5",
|
||||
16 -> "a6",
|
||||
17 -> "a7",
|
||||
18 -> "s2",
|
||||
19 -> "s3",
|
||||
20 -> "s4",
|
||||
21 -> "s5",
|
||||
22 -> "s6",
|
||||
23 -> "s7",
|
||||
24 -> "s8",
|
||||
25 -> "s9",
|
||||
26 -> "s10",
|
||||
27 -> "s11",
|
||||
28 -> "t3",
|
||||
29 -> "t4",
|
||||
30 -> "t5",
|
||||
31 -> "t6",
|
||||
)
|
||||
|
||||
}
|
||||
|
103
src/test/scala/RISCV/testRunner.scala
Normal file
103
src/test/scala/RISCV/testRunner.scala
Normal file
|
@ -0,0 +1,103 @@
|
|||
package FiveStage
|
||||
import org.scalatest.{Matchers, FlatSpec}
|
||||
import cats._
|
||||
import cats.implicits._
|
||||
import fileUtils._
|
||||
|
||||
import chisel3.iotesters._
|
||||
import scala.collection.mutable.LinkedHashMap
|
||||
|
||||
import fansi.Str
|
||||
|
||||
import Ops._
|
||||
import Data._
|
||||
import VM._
|
||||
|
||||
import PrintUtils._
|
||||
import LogParser._
|
||||
|
||||
case class TestOptions(
|
||||
printIfSuccessful : Boolean,
|
||||
printErrors : Boolean,
|
||||
printParsedProgram : Boolean,
|
||||
printVMtrace : Boolean,
|
||||
printVMfinal : Boolean,
|
||||
printMergedTrace : Boolean,
|
||||
nopPadded : Boolean,
|
||||
breakPoints : List[Int], // Not implemented
|
||||
testName : String
|
||||
)
|
||||
|
||||
case class TestResult(
|
||||
regError : Option[String],
|
||||
memError : Option[String],
|
||||
program : String,
|
||||
vmTrace : String,
|
||||
vmFinal : String,
|
||||
sideBySide : String
|
||||
)
|
||||
|
||||
object TestRunner {
|
||||
|
||||
def run(testOptions: TestOptions): Boolean = {
|
||||
|
||||
val testResults = for {
|
||||
lines <- fileUtils.readTest(testOptions)
|
||||
program <- FiveStage.Parser.parseProgram(lines, testOptions)
|
||||
(binary, (trace, finalVM)) <- program.validate.map(x => (x._1, x._2.run))
|
||||
(termitationCause, chiselTrace) <- ChiselTestRunner(
|
||||
binary.toList.sortBy(_._1.value).map(_._2),
|
||||
program.settings,
|
||||
finalVM.pc,
|
||||
1500)
|
||||
} yield {
|
||||
val traces = mergeTraces(trace, chiselTrace).map(x => printMergedTraces((x), program))
|
||||
|
||||
val programString = printProgram(program)
|
||||
val vmTraceString = printVMtrace(trace, program)
|
||||
val vmFinalState = finalVM.regs.show
|
||||
val traceString = printLogSideBySide(trace, chiselTrace, program)
|
||||
|
||||
val regError = compareRegs(trace, chiselTrace)
|
||||
val memError = compareMem(trace, chiselTrace)
|
||||
|
||||
TestResult(
|
||||
regError,
|
||||
memError,
|
||||
programString,
|
||||
vmTraceString,
|
||||
vmFinalState.toString,
|
||||
traceString)
|
||||
}
|
||||
|
||||
testResults.left.foreach{ error =>
|
||||
say(s"Test was unable to run due to error: $error")
|
||||
}
|
||||
|
||||
testResults.map{ testResults =>
|
||||
val successful = List(testResults.regError, testResults.memError).flatten.headOption.map(_ => false).getOrElse(true)
|
||||
if(successful)
|
||||
say(s"${testOptions.testName} succesful")
|
||||
else
|
||||
say(s"${testOptions.testName} failed")
|
||||
|
||||
if(testOptions.printIfSuccessful && successful){
|
||||
if(testOptions.printParsedProgram) say(testResults.program)
|
||||
if(testOptions.printVMtrace) say(testResults.vmTrace)
|
||||
if(testOptions.printVMfinal) say(testResults.vmFinal)
|
||||
if(testOptions.printMergedTrace) say(testResults.sideBySide)
|
||||
}
|
||||
else{
|
||||
if(testOptions.printErrors){
|
||||
say(testResults.regError.map(_.show.toString).getOrElse("no reg errors"))
|
||||
say(testResults.memError.map(_.show.toString).getOrElse("no mem errors"))
|
||||
}
|
||||
if(testOptions.printParsedProgram) say(testResults.program)
|
||||
if(testOptions.printVMtrace) say(testResults.vmTrace)
|
||||
if(testOptions.printVMfinal) say(testResults.vmFinal)
|
||||
if(testOptions.printMergedTrace) say(testResults.sideBySide)
|
||||
}
|
||||
successful
|
||||
}.toOption.getOrElse(false)
|
||||
}
|
||||
}
|
166
src/test/scala/TestUtils.scala
Normal file
166
src/test/scala/TestUtils.scala
Normal file
|
@ -0,0 +1,166 @@
|
|||
package FiveStage
|
||||
|
||||
import fileUtils._
|
||||
import Data._
|
||||
import PrintUtils._
|
||||
|
||||
object TestUtils {
|
||||
|
||||
/**
|
||||
* Generate and serialize BTrees for the test runner
|
||||
*/
|
||||
def generateBTree: Unit = {
|
||||
|
||||
import cats._
|
||||
import cats.implicits._
|
||||
|
||||
case class AnnotatedNode(value: Int, index: Int, left: Option[AnnotatedNode], right: Option[AnnotatedNode]){
|
||||
def find(key: Int): Unit = {
|
||||
say(s"at $index (${(4 + (index << 2)).hs})-> ${value.hs}, looking for ${key.hs}")
|
||||
if(value == key)
|
||||
say("found it")
|
||||
else if(key < value)
|
||||
left.map{x => say("going left\n"); x.find(key)}.getOrElse("gave up")
|
||||
else
|
||||
right.map{x => say("going right\n"); x.find(key)}.getOrElse("gave up")
|
||||
}
|
||||
}
|
||||
|
||||
def printAnnoTree(tree: AnnotatedNode, depth: Int): String = {
|
||||
val ls = tree.left.map(n => printAnnoTree(n, depth + 2)).getOrElse("left empty")
|
||||
val rs = tree.right.map(n => printAnnoTree(n, depth + 2)).getOrElse("right empty")
|
||||
val pads = "|".padTo(2, ' ')*depth
|
||||
s"${tree.value.hs} at ${tree.index.hs}\n$pads$ls\n$pads$rs"
|
||||
}
|
||||
|
||||
case class Node(value: Int, left: Option[Node], right: Option[Node]){
|
||||
def append(v: Int): Node = if(v < value)
|
||||
copy(left = left.map(_.append(v)).orElse(Some(Node(v))))
|
||||
else
|
||||
copy(right = right.map(_.append(v)).orElse(Some(Node(v))))
|
||||
}
|
||||
|
||||
object Node {
|
||||
def apply(v: Int): Node = Node(v, None, None)
|
||||
}
|
||||
|
||||
def annotate(n: Int, root: Node): (AnnotatedNode, Int) = {
|
||||
(root.left, root.right) match {
|
||||
case (None, None) => {
|
||||
(AnnotatedNode(root.value, n, None, None), n + 1)
|
||||
}
|
||||
case (Some(node), None) => {
|
||||
val (annotated, next) = annotate(n+1, node)
|
||||
(AnnotatedNode(root.value, n, Some(annotated), None), next)
|
||||
}
|
||||
case (None, Some(node)) => {
|
||||
val (annotated, next) = annotate(n+1, node)
|
||||
(AnnotatedNode(root.value, n, None, Some(annotated)), next)
|
||||
}
|
||||
case (Some(left), Some(right)) => {
|
||||
val (leftAnno, leftNext) = annotate(n+1, left)
|
||||
val (rightAnno, rightNext) = annotate(leftNext, right)
|
||||
(AnnotatedNode(root.value, n, Some(leftAnno), Some(rightAnno)), rightNext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def foldAnno(root: Option[AnnotatedNode]): List[Int] = {
|
||||
root.map{ root =>
|
||||
val leftIndex = root.left.map(_.index).getOrElse(0)
|
||||
val hasLeft = root.left.map(_ => 1).getOrElse(0)
|
||||
|
||||
val rightIndex = root.right.map(_.index).getOrElse(0)
|
||||
val hasRight = root.right.map(_ => 1).getOrElse(0)
|
||||
|
||||
val entry = hasLeft + (leftIndex << 1) + (hasRight << 8) + (rightIndex << 9) + (root.value << 16)
|
||||
|
||||
say(s"with leftIndex: ${leftIndex.hs}, rightIndex: ${rightIndex.hs}, value: ${root.value.hs} we got ${entry.hs}")
|
||||
|
||||
entry :: foldAnno(root.left) ::: foldAnno(root.right)
|
||||
}.getOrElse(Nil)
|
||||
}
|
||||
|
||||
import scala.util.Random
|
||||
val r = new scala.util.Random(0xF01D1EF7)
|
||||
def randInt = r.nextInt(1000)
|
||||
|
||||
val seed = (0 to 100).map(_ => randInt).toList
|
||||
val btree = seed.foldLeft(Node(randInt, None, None)){ case(acc, n) => acc.append(n)}
|
||||
|
||||
val annoTree = annotate(0, btree)
|
||||
say(foldAnno(Some(annoTree._1)).zipWithIndex.map{case(m, idx) => s"#memset ${(4 + (idx*4)).hs}, ${m.hs}"}.mkStringN)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random program filled with hazards
|
||||
*/
|
||||
def generateHazardsForward(steps: Int) : Unit = {
|
||||
|
||||
// val r = new scala.util.Random(0xF01D1EF7)
|
||||
val r = new scala.util.Random(0xF01D1EF8)
|
||||
import Ops._
|
||||
|
||||
val active = List(1, 2, 3)
|
||||
|
||||
val initVM = {
|
||||
val init = VM.init
|
||||
val init1 = init.copy(regs = (init.regs + (Reg(1) -> 123))._2)
|
||||
val init2 = init.copy(regs = (init1.regs + (Reg(2) -> -40))._2)
|
||||
val init3 = init.copy(regs = (init2.regs + (Reg(3) -> 0xFFEE))._2)
|
||||
init3
|
||||
}
|
||||
|
||||
|
||||
def generateInstruction: (Int, (String, Op)) = {
|
||||
val rd = active.shuffle(r).head
|
||||
val rs1 = active.shuffle(r).head
|
||||
val rs2 = active.shuffle(r).head
|
||||
val imm = r.nextInt(1024) - 512
|
||||
val shift = r.nextInt(32) - 16
|
||||
|
||||
val choices = List(
|
||||
(s"add ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.add(rd, rs1, rs2)),
|
||||
(s"sub ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.sub(rd, rs1, rs2)),
|
||||
(s"or ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.or(rd, rs1, rs2)),
|
||||
(s"xor ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.xor(rd, rs1, rs2)),
|
||||
(s"and ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.and(rd, rs1, rs2)),
|
||||
(s"sll ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.sll(rd, rs1, rs2)),
|
||||
(s"srl ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.srl(rd, rs1, rs2)),
|
||||
(s"sra ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.sra(rd, rs1, rs2)),
|
||||
(s"slt ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.slt(rd, rs1, rs2)),
|
||||
(s"sltu ${Reg(rd).show}, ${Reg(rs1).show}, ${Reg(rs2).show}", Arith.sltu(rd, rs1, rs2)),
|
||||
|
||||
(s"addi ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.add(rd, rs1, imm)),
|
||||
(s"ori ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.or(rd, rs1, imm)),
|
||||
(s"xori ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.xor(rd, rs1, imm)),
|
||||
(s"andi ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.and(rd, rs1, imm)),
|
||||
(s"slli ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(shift).show}", ArithImm.sll(rd, rs1, shift)),
|
||||
(s"srli ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(shift).show}", ArithImm.srl(rd, rs1, shift)),
|
||||
(s"srai ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(shift).show}", ArithImm.sra(rd, rs1, shift)),
|
||||
(s"slti ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.slt(rd, rs1, imm)),
|
||||
(s"sltiu ${Reg(rd).show}, ${Reg(rs1).show}, ${Imm(imm).show}", ArithImm.sltu(rd, rs1, imm)))
|
||||
(rd, choices.shuffle(r).head)
|
||||
}
|
||||
|
||||
def helper(attempts: Int, steps: Int, vm: VM): Unit = {
|
||||
if((attempts > 10) || (steps == 0))
|
||||
()
|
||||
else{
|
||||
val (nextRd, (nS, nextOp)) = generateInstruction
|
||||
val withOp = vm.copy(imem = vm.imem + (vm.pc -> nextOp))
|
||||
val nextVmE = withOp.stepInstruction
|
||||
val nextVm = nextVmE.toOption.get.run._2
|
||||
if(nextVm.regs.repr(Reg(nextRd)) == 0)
|
||||
helper(attempts + 1, steps, vm)
|
||||
else {
|
||||
say(nS)
|
||||
helper(0, steps - 1, nextVm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
helper(0, steps, initVM)
|
||||
}
|
||||
}
|
188
src/test/scala/chiselTestRunner.scala
Normal file
188
src/test/scala/chiselTestRunner.scala
Normal file
|
@ -0,0 +1,188 @@
|
|||
package FiveStage
|
||||
import chisel3.iotesters._
|
||||
import scala.collection.mutable.LinkedHashMap
|
||||
|
||||
import fileUtils.say
|
||||
|
||||
import Data._
|
||||
import PrintUtils._
|
||||
|
||||
private class ChiselTestRunner (
|
||||
instructions : List[Int],
|
||||
settings : List[TestSetting],
|
||||
c : Tile,
|
||||
d : PeekPokeTester[Tile],
|
||||
terminalAddress : Addr,
|
||||
maxSteps : Int) {
|
||||
|
||||
|
||||
/**
|
||||
* Currently unused as it is quite nontrivial to figure out whether read data is actually used.
|
||||
* Still, with the necessary scaffolding these can be correlated with reg writes to figure out
|
||||
* if they are valid or not.
|
||||
*/
|
||||
case class ChiselMemReadEvent(pcAddr: Addr, memAddr: Addr, word: Int)
|
||||
|
||||
private def setup = {
|
||||
|
||||
d.poke(d.dut.io.setup, 1)
|
||||
|
||||
val initDMem = DMem(settings).repr.toList
|
||||
val initRegs = Regs(settings).repr.toList
|
||||
|
||||
setupImem(instructions)
|
||||
setupRegs(initRegs)
|
||||
setupDmem(initDMem)
|
||||
|
||||
// flush
|
||||
d.step(1)
|
||||
disableTestSignals
|
||||
d.step(5)
|
||||
d.poke(d.dut.io.setup, 0)
|
||||
|
||||
def disableTestSignals: Unit = {
|
||||
d.poke(d.dut.io.DMEMWriteData, 0)
|
||||
d.poke(d.dut.io.DMEMAddress, 0)
|
||||
d.poke(d.dut.io.DMEMWriteEnable, 0)
|
||||
d.poke(d.dut.io.regsWriteData, 0)
|
||||
d.poke(d.dut.io.regsAddress, 0)
|
||||
d.poke(d.dut.io.regsWriteEnable, 0)
|
||||
d.poke(d.dut.io.IMEMWriteData, 0)
|
||||
d.poke(d.dut.io.IMEMAddress, 4092)
|
||||
}
|
||||
|
||||
def setupRegs(regs: List[(Reg, Int)]) = {
|
||||
regs.foreach{ case(reg, word) =>
|
||||
d.poke(d.dut.io.setup, 1)
|
||||
d.poke(d.dut.io.regsWriteEnable, 1)
|
||||
d.poke(d.dut.io.regsWriteData, BigInt(word))
|
||||
d.poke(d.dut.io.regsAddress, reg.value)
|
||||
d.step(1)
|
||||
}
|
||||
d.poke(d.dut.io.regsWriteEnable, 0)
|
||||
d.poke(d.dut.io.regsWriteData, 0)
|
||||
d.poke(d.dut.io.regsAddress, 0)
|
||||
}
|
||||
|
||||
def setupDmem(mem: List[(Addr, Int)]) = {
|
||||
mem.foreach { case (addr, word) =>
|
||||
d.poke(d.dut.io.setup, 1)
|
||||
d.poke(d.dut.io.DMEMWriteEnable, 1)
|
||||
d.poke(d.dut.io.DMEMWriteData, word)
|
||||
d.poke(d.dut.io.DMEMAddress, addr.value)
|
||||
d.step(1)
|
||||
}
|
||||
d.poke(d.dut.io.DMEMWriteEnable, 0)
|
||||
d.poke(d.dut.io.DMEMWriteData, 0)
|
||||
d.poke(d.dut.io.DMEMAddress, 0)
|
||||
|
||||
}
|
||||
|
||||
def setupImem(instructions: List[Int]) = {
|
||||
(0 until instructions.length).foreach{ ii =>
|
||||
d.poke(d.dut.io.IMEMAddress, ii*4)
|
||||
d.poke(d.dut.io.IMEMWriteData, instructions(ii).toInt)
|
||||
d.step(1)
|
||||
}
|
||||
d.poke(d.dut.io.IMEMAddress, 4092) // Ensures that we don't overwrite an instruction. Bandaid for lack of IMEM.writeEnable
|
||||
d.poke(d.dut.io.IMEMWriteData, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private def getPC = Addr(d.peek(d.dut.io.currentPC).toInt)
|
||||
private def getDMemWriteAddress = Addr(d.peek(d.dut.io.memDeviceWriteAddress).toInt)
|
||||
private def getRegWriteAddress = Reg(d.peek(d.dut.io.regsDeviceWriteAddress).toInt)
|
||||
|
||||
private def getRegUpdate: Option[ChiselRegEvent] = {
|
||||
if(
|
||||
(d.peek(d.dut.io.regsDeviceWriteEnable) == 1) &&
|
||||
(getRegWriteAddress.value != 0)
|
||||
){
|
||||
val regWriteAddress = getRegWriteAddress
|
||||
val regWriteData = d.peek(d.dut.io.regsDeviceWriteData).toInt
|
||||
val regUpdate = ChiselRegEvent(getPC, regWriteAddress, regWriteData)
|
||||
Some(regUpdate)
|
||||
}
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
private def getMemWrite: Option[ChiselMemWriteEvent] = {
|
||||
if(d.peek(d.dut.io.memDeviceWriteEnable) == 1) {
|
||||
val memWriteAddress = getDMemWriteAddress
|
||||
val memWriteData = d.peek(d.dut.io.memDeviceWriteData).toInt
|
||||
val memUpdate = ChiselMemWriteEvent(getPC, memWriteAddress, memWriteData)
|
||||
Some(memUpdate)
|
||||
}
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
private def stepOne: CircuitTrace = {
|
||||
val state = (getPC, List(getRegUpdate, getMemWrite).flatten)
|
||||
// say(d.peek(d.dut.io).toList.mkStringN)
|
||||
d.step(1)
|
||||
state
|
||||
}
|
||||
|
||||
|
||||
private def go(log: List[CircuitTrace], timeOut: Int): (Option[String], List[CircuitTrace]) = {
|
||||
if(timeOut == 0){
|
||||
(Some("Chisel tester timed out before reaching termination address"), log.reverse)
|
||||
}
|
||||
else if(getPC == terminalAddress){
|
||||
(None, (flush ::: log).reverse)
|
||||
}
|
||||
else {
|
||||
val step = stepOne
|
||||
go(step :: log, timeOut - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// After finishing, let the circuit run until all updates can be committed.
|
||||
private def flush: List[CircuitTrace] =
|
||||
(0 to 3).map(_ => stepOne).reverse.toList
|
||||
|
||||
/**
|
||||
* Run the entire shebang
|
||||
*/
|
||||
def run: (Option[String], List[CircuitTrace]) = {
|
||||
setup
|
||||
go(Nil, maxSteps)
|
||||
}
|
||||
}
|
||||
|
||||
object ChiselTestRunner {
|
||||
|
||||
def apply(
|
||||
binary : List[Int],
|
||||
settings : List[TestSetting],
|
||||
terminalAddress : Addr,
|
||||
maxSteps : Int): Either[String, (Option[String], List[CircuitTrace])] = {
|
||||
|
||||
var sideEffectExtravaganza: Option[(Option[String], List[CircuitTrace])] = None
|
||||
|
||||
val error: Either[String, Boolean] = scala.util.Try {
|
||||
chisel3.iotesters.Driver(() => new Tile(), "treadle") { c =>
|
||||
new PeekPokeTester(c) {
|
||||
val testRunner = new ChiselTestRunner(
|
||||
binary,
|
||||
settings,
|
||||
c,
|
||||
this,
|
||||
terminalAddress,
|
||||
maxSteps
|
||||
)
|
||||
|
||||
val log = testRunner.run
|
||||
sideEffectExtravaganza = Some(log)
|
||||
}
|
||||
}
|
||||
}.toEither.left.map{printChiselError}
|
||||
|
||||
error.flatMap{ e =>
|
||||
sideEffectExtravaganza.toRight("Unknown test failure, please let me know")
|
||||
}
|
||||
}
|
||||
}
|
94
src/test/scala/fileUtils.scala
Normal file
94
src/test/scala/fileUtils.scala
Normal file
|
@ -0,0 +1,94 @@
|
|||
package FiveStage
|
||||
import chisel3.iotesters._
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import scala.collection.mutable.LinkedHashMap
|
||||
// import cats.effect.ContextShift
|
||||
|
||||
import cats.implicits._
|
||||
import cats._
|
||||
import cats.syntax._
|
||||
import cats.Applicative._
|
||||
import atto._, Atto._
|
||||
|
||||
object fileUtils {
|
||||
|
||||
def say(word: Any)(implicit filename: sourcecode.File, line: sourcecode.Line): Unit = {
|
||||
val fname = filename.value.split("/").last
|
||||
println(Console.YELLOW + s"[${fname}: ${sourcecode.Line()}]" + Console.RESET + s" - $word")
|
||||
}
|
||||
|
||||
def sayRed(word: Any)(implicit filename: sourcecode.File, line: sourcecode.Line): Unit = {
|
||||
val fname = filename.value.split("/").last
|
||||
println(Console.YELLOW + s"[${fname}: ${sourcecode.Line()}]" + Console.RED + s" - $word")
|
||||
}
|
||||
def sayGreen(word: Any)(implicit filename: sourcecode.File, line: sourcecode.Line): Unit = {
|
||||
val fname = filename.value.split("/").last
|
||||
println(Console.YELLOW + s"[${fname}: ${sourcecode.Line()}]" + Console.GREEN + s" - $word")
|
||||
}
|
||||
|
||||
def getListOfFiles(dir: String): List[File] =
|
||||
(new File(dir)).listFiles.filter(_.isFile).toList
|
||||
|
||||
def getListOfFiles(dir: Path): List[File] =
|
||||
dir.toFile().listFiles.filter(_.isFile).toList
|
||||
|
||||
|
||||
def getListOfFolders(dir: String): List[File] =
|
||||
(new File(dir)).listFiles.filter(_.isDirectory).toList
|
||||
|
||||
def getListOfFolders(dir: Path): List[File] =
|
||||
dir.toFile().listFiles.filter(_.isDirectory).toList
|
||||
|
||||
def getListOfFilesRecursive(dir: String): List[File] = {
|
||||
getListOfFiles(dir) ::: getListOfFolders(dir).flatMap(f =>
|
||||
getListOfFilesRecursive(f.getPath)
|
||||
)
|
||||
}
|
||||
|
||||
import cats.implicits._
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.Executors
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
def relativeFile(name: String) = {
|
||||
new File(getClass.getClassLoader.getResource(name).getPath)
|
||||
}
|
||||
|
||||
def getTestDir: File =
|
||||
new File(getClass.getClassLoader.getResource("tests").getPath)
|
||||
|
||||
def getAllTests: List[File] = getListOfFilesRecursive(getTestDir.getPath)
|
||||
.filter( f => f.getPath.endsWith(".s") )
|
||||
|
||||
def getAllTestNames: List[String] = getAllTests.map(_.toString.split("/").takeRight(1).mkString)
|
||||
|
||||
def clearTestResults = {
|
||||
try {
|
||||
val testResults = relativeFile("/testResults")
|
||||
testResults.delete()
|
||||
}
|
||||
catch {
|
||||
case _:Throwable => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an assembly file.
|
||||
*/
|
||||
def readTest(testOptions: TestOptions): Either[String, List[String]] = {
|
||||
|
||||
// Ahh, the GNU toolchain and its tabs
|
||||
val annoyingTabCharacter = ' '
|
||||
|
||||
getAllTests.filter(_.getName.contains(testOptions.testName)).headOption.toRight(s"File not found: ${testOptions.testName}").flatMap{ filename =>
|
||||
import scala.io.Source
|
||||
scala.util.Try(
|
||||
Source.fromFile(filename)
|
||||
.getLines.toList
|
||||
.map(_.replace(annoyingTabCharacter, ' ')))
|
||||
.toOption
|
||||
.toRight(s"Error reading $filename")
|
||||
}
|
||||
}
|
||||
}
|
43
src/test/scala/testConf.scala
Normal file
43
src/test/scala/testConf.scala
Normal file
|
@ -0,0 +1,43 @@
|
|||
// package FiveStage
|
||||
// import chisel3._
|
||||
// import chisel3.iotesters._
|
||||
// import org.scalatest.{Matchers, FlatSpec}
|
||||
// import spire.math.{UInt => Uint}
|
||||
// import fileUtils._
|
||||
// import cats.implicits._
|
||||
|
||||
// import RISCVutils._
|
||||
// import RISCVasm._
|
||||
// import riscala._
|
||||
|
||||
// import utilz._
|
||||
|
||||
// class AllTests extends FlatSpec with Matchers {
|
||||
|
||||
// val results = fileUtils.getAllTests.map{f =>
|
||||
// val result = TestRunner.runTest(f.getPath, false)
|
||||
// (f.getName, result)
|
||||
// }
|
||||
|
||||
// makeReport(results)
|
||||
// }
|
||||
|
||||
|
||||
// /**
|
||||
// This is for you to run more verbose testing.
|
||||
// */
|
||||
// class SelectedTests extends FlatSpec with Matchers {
|
||||
|
||||
// val tests = List(
|
||||
// "matMul.s"
|
||||
// )
|
||||
|
||||
// if(!tests.isEmpty){
|
||||
// val results = fileUtils.getAllTests.filter(f => tests.contains(f.getName)).map{ f =>
|
||||
// val result = TestRunner.runTest(f.getPath, true)
|
||||
// (f.getName, result)
|
||||
// }
|
||||
|
||||
// makeReport(results)
|
||||
// }
|
||||
// }
|
Loading…
Add table
Add a link
Reference in a new issue