This commit is contained in:
peteraa 2019-06-07 17:43:33 +02:00
commit 932413bb3d
61 changed files with 7249 additions and 0 deletions

355
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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.

View 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
View file

@ -0,0 +1 @@
sbt.version = 1.1.0

578
sbt.sh Executable file
View 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
View 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
*/
}

View 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
View 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
}
}

View 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
View 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
View 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
View 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
View 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
}

View 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) }
}

View 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
View 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
}

View 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)
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View 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);
}

View 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);
}
}

View 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);
}

View 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;
}

View 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;
}

View 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

View 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)
}
}
}
}

View 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)
}
}

View 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
}
}

View 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) }
}

View 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)
}
}

View 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)
}
}

View 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
}
}

View 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))
}
}
}

View 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)
}
}

View 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")
}
}

View 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",
)
}

View 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)
}
}

View 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)
}
}

View 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")
}
}
}

View 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")
}
}
}

View 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)
// }
// }