rubbler
is a RISC-V assembler written in Rust 🦀. This library was written with the main purpose of embedding a simple RISC-V assembler inside of a RISC-V CPU test bench code written with verilator.
In addition, a command line program called rubble
can also be installed. rubble
reads lines from standard input and outputs a string of '1's and '0's representing the assembled code to standard output.
$ rubble
> Input RISC-V assembly line(s): (Press <CTRL-d> once finished)
add t0, t1, 5
> Rubbling...
[Line 1] Generator error: `add` instruction expects the following arguments: [RegDest, RegSrc1, RegSrc2].
$ rubble
> Input RISC-V assembly line(s): (Press <CTRL-d> once finished)
jal t0, hello
> Rubbling...
[Line 1] Generator error: Cannot resolve symbol: `hello`.
$ rubble
> Input RISC-V assembly line(s): (Press <CTRL-d> once finished)
hello
> Rubbling...
[Line 1] Syntax error: Unknown opcode or directive `hello`.
$ rubble
> Input RISC-V assembly line(s): (Press <CTRL-d> once finished)
fibonacci:
addi t0, zero, 0 # t0
addi t1, zero, 1 # t1
addi t2, zero, 0 # i
loop:
bge t2, a0, end
add t3, t0, t1 # temp
addi t0, t1, 0
addi t1, t3, 0
addi t2, t2, 1
jal t4, loop
end:
addi a0, t0, 0
> Rubbling...
> Here's your bytes:
10010011000000100000000000000000
00010011000000110001000000000000
10010011000000110000000000000000
01100011110111001010001100000000
00110011100011100110001000000000
10010011000000100000001100000000
00010011000000110000111000000000
10010011100000110001001100000000
11101111111111101101111111111110
00010011100001010000001000000000
$ rubble
> Input RISC-V assembly line(s): (Press <CTRL-d> once finished)
.section .data
.byte 1, 2, 3
> Rubbling...
> Here's your bytes:
000000010000001000000011
See Supported directives for all supported directives.
Right now, only a subset of RV32I are supported:
- Register‒Immediate arithmetic operations
- ADDI, ANDI, ORI, XORI, SLTI, SLTIU, SLLI, SRLI, LUI, AUIPC
- Register‒Register arithmetic operation
- ADD, AND, OR, XOR, SLT, SLTU, SLL, SRL, SRA, SUB
- Jump instructions
- JAL, JALR
- Branch instructions
- BEQ, BNE, BLT, BLTU, BGE, BGEU
- Load operations
- LW, LH, LHU, LB, LBU
- Store operations
- SW, SH, SB
Right now, only the following directives are available for use:
- .align
- .p2align
- .comm
- .common
- .section
- .equ
- .byte
See RISC-V Assembly Programmer's Manual for the syntax of each directives.
Rust (see installing Rust)
git clone https://github.com/fuad1502/rubbler.git
cd rubbler
cargo build --release
These commands outputs rubbler/target/release/rubble
binary, rubbler/target/rubbler.h
header and rubbler/target/release/librubbler.a
static library.
let source = "lui t2, -3";
let expected_res = vec![0b10110111,0b11110011,0b11111111,0b11111111];
let res = rubbler::rubble(source).unwrap();
assert_eq!(res, expected_res);
For more examples in Rust, see docs.rs/rubbler.
This example is a snippet taken from rubbler-verilator-example.
#include "rubbler.h"
...
auto source = "add t0, t1, t2";
auto bytes = (uint8_t *)malloc(sizeof(uint8_t) * MAX_SIZE);
uintptr_t size = MAX_SIZE;
assert(rubble(source, bytes, &size));
...
See docs.rs/rubbler rubbler::ffi
module for a complete explanation on how to use each function, or simply look at the documentation string of each function in rubbler.h
.
In the following discussion, we assume you have installed verilator
, and the verilator
command is available. See the verilator doccumentation for instructions on doing so. Both of the following example is taken from rubbler-verilator-example.
Assuming the following project directory structure:
- project
- main.cpp
- top.sv
- Makefile
The following Makefile
will build main
from main.cpp
and top.sv
that can use both verilator
and rubbler
library from main.cpp
.
VERILATOR_ROOT := /usr/local/share/verilator
VM_SC := 0
VM_TRACE := 0
VERILATOR_OBJS := verilated.o verilated_threads.o verilated_dpi.o
main:main.o obj_dir/Vtop__ALL.a librubbler.a $(VERILATOR_OBJS)
$(CXX) $^ -o $@
main.o: main.cpp obj_dir/Vtop.h rubbler.h
$(CXX) \
-Iobj_dir \
-I$(VERILATOR_ROOT)/include \
-I$(VERILATOR_ROOT)/include/vltstd \
-c main.cpp -o $@
obj_dir/Vtop__ALL.a obj_dir/Vtop.h: top.sv
verilator -cc --build -j top.sv
rubbler.h librubbler.a: rubbler
cd rubbler && cargo build --release
cp rubbler/target/rubbler.h rubbler/target/release/librubbler.a .
rubbler:
git clone https://github.com/fuad1502/rubbler.git
include $(VERILATOR_ROOT)/include/verilated.mk
.PHONY:clean
clean:
rm -rf obj_dir
rm -rf rubbler
rm librubbler.a rubbler.h
rm *.o *.d
rm main
See rubbler-verilator-example. for the complete example.
Assuming the following project directory structure:
- project
- main.cpp
- top.sv
- CMakeLists.txt
The following CMakeLists.txt
file will build main
from main.cpp
and top.sv
that can use both verilator
and rubbler
library from main.cpp
.
cmake_minimum_required(VERSION 3.14)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(rubbler-verilator-example)
find_package(verilator REQUIRED)
include(ExternalProject)
ExternalProject_Add(
rubbler
GIT_REPOSITORY https://github.com/fuad1502/rubbler.git
DOWNLOAD_DIR ${CMAKE_BINARY_DIR}
SOURCE_DIR ${CMAKE_BINARY_DIR}/rubbler
BINARY_DIR ${CMAKE_BINARY_DIR}/rubbler
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
BUILD_COMMAND cargo build --release
)
set(RUBBLER_LIB ${CMAKE_BINARY_DIR}/rubbler/target/release/librubbler.a)
add_executable(main main.cpp)
target_include_directories(main PRIVATE ${CMAKE_BINARY_DIR}/rubbler/target)
target_link_libraries(main PRIVATE ${RUBBLER_LIB})
verilate(main SOURCES top.sv)
See rubbler-verilator-example. for the complete example.
To use the rubble
binary system wide, install it with the following command:
cargo install rubbler
- Using symbols inside memory addressing expressions is not yet supported.
- Report multiple errors instead of terminating on the first detected error.
- Increase error reporting verbosity by adding column information and show the offending line together with the report.
- Support pseudo instructions.