A RISC-V assembler library for Scala/Chisel HDL projects. For details, check the scaladoc.
Leveraging the library, I have a Web application built in Scala.js that runs in the browser to assemble web instructions to hex. Check it out at https://carlosedp.github.io/rvasmweb/. The source code for the site is here.
When using SBT, add the following lines to your build.sbt
file.
// Import libraries
libraryDependencies += "com.carlosedp" %% "riscvassembler" % "1.10.0" //ReleaseVerSBT
If you use mill
build tool, add the following dep to your build.sc
:
// Add to your ivyDeps
def ivyDeps = Agg(
ivy"com.carlosedp::riscvassembler:1.10.0" //ReleaseVerMill
...
)
If using the library for Scala.js or Scala Native projects, remember to use "::" (double colon) on Mill between the library name and version and on SBT use "%%%" (triple percent) between organization and the library name.
The library is pure Scala and provides methods to generate hexadecimal machine code (like memory files to be consumed by readmemh
statements) from assembly input. It does not depend on Chisel or other libs and intent to work similarly to a simpler gcc + ld + objcopy + hexdump
flow as used on this Makefile
or https://riscvasm.lucasteske.dev web app.
The library can be seen in use in ChiselV, my RV32I core written in Chisel. The core tests use the library to generate test data.
What the library can and can not do:
- The library can generate hex(machine code) for most RV32 instructions;
- It can accept either offsets or labels (in the same or previous line) for jump/branch instructions;
- It can implement some pseudo-instructions (more to come soon);
- It can generate one machine code for each input asm instruction;
- It can not decompose one pseudo-instruction to multiple instructions. Eg.
li x1, 0x80000000
toaddiw ra,zero,1
+ra,ra,0x1f
as gcc does; - It can not validate your input asm code. Imm values might get truncated if not proper used;
- It can not support assembler relocation functions;
- The library ignores all asm directives.
The program can be a single line or multi-line statements(supports inline or full-line comments) and can be generated from a simple string, multi-line string or loaded from a file.
Reading from file:
; Sample file "input.asm":
addi x0, x0, 0
addi x1, x1, 1
addi x2, x2, 2
// Using the lib
val outputHex = RISCVAssembler.fromFile("input.asm")
// outputHex will be:
// 00000013
// 00108093
// 00210113
From a multiline string:
// Sample input:
val input =
"""addi x0, x0, 0
addi x1, x1, 1
addi x2, x2, 2
""".stripMargin
val output = RISCVAssembler.fromString(input)
// outputHex will be:
// 00000013
// 00108093
// 00210113
Which can be used as the input to tests in a RISC-V Core.
The library also contains a command line tool that generates the machine code from strings or input files.
❯ rvasmcli --help
RISC-V Assembler for Scala
main
This tool parses input strings or files in RISC-V assembly language generating hexadecimal machine
code.
-a --assembly <str> Assembly instruction string in quotes(can be multiple instructions separated
by `\n`
-f --file-in <str> Assembly file input
-o --file-out <str> If defined, output will be redirected to this file (overwrite if exists)
❯ rvasmcli -a "addi x1, x2, 32\njal x0, 128"
RISC-V Assembler for Scala
Generated Output:
02010093
0800006F
To generate the tool binary yourself, use ./mill Alias/run bin
and the native executable will be generated and it's name printed on screen.
Native binaries for major OS/Arch combinations will be published soon.
Snapshot versions are released on every commit to main branch and might be broken (check CI). If you want to use it, configure as follows.
Add the new Sonatype repository to your build.sbt
resolvers and change the library import name:
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases"),
"Sonatype New OSS Snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots"
)
// and change the dependency to latest SNAPSHOT as:
libraryDependencies += "com.carlosedp" %% "riscvassembler" % "1.11-SNAPSHOT" //SnapshotVerSBT
Confirm the latest versions displayed on the badges at the top of this readme for both stable and snapshot (without the leading "v").
If you use mill
build tool, add the following dep to your build.sc
:
import coursier.MavenRepository
...
// Inside your project `object`:
// And add the snapshot resolver if using it
def repositoriesTask = T.task { super.repositoriesTask() ++ Seq(
MavenRepository("https://s01.oss.sonatype.org/content/repositories/snapshots")
) }
def ivyDeps = Agg(
ivy"com.carlosedp::riscvassembler:1.11-SNAPSHOT" //SnapshotVerMill
...
)
If using the library for Scala.js or Scala Native projects, remember to use "::" (double colon) on Mill between the library name and version and on SBT use "%%%" (triple percent) between organization and the library name.
All build processes are integrated into mill build.sc
. There are tasks for linting, code coverage, publishing and binary generation.
To locally test and build the library for Scala.js, it's required to have nodejs. After install, run npm install
so dependencies are installed as well.
To test and generate the Scala Native binaries, the LLVM toolchain is required.
Publishing flow:
- Commit and push latest changes to
main
(generates SNAPSHOT) - Git tag new version
- Push tag to origin (generates a new release)
- Check if readme was updated
The library has been published to Maven Central thru Sonatype: