# Rubbler
`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](https://github.com/verilator/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.
## Features
### Verbose error explanation
```
$ 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`.
```
### Supports labels
```
$ 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
```
### Supports directives
```
$ 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](#supported-directives) for all supported directives.
## Supported instructions
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
## Supported assembly directives
Right now, only the following directives are available for use:
- .align
- .p2align
- .comm
- .common
- .section
- .equ
- .byte
See [RISC-V Assembly Programmer's Manual](https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md) for the syntax of each directives.
## Building from source
### Requirements
Rust (see [installing Rust](https://www.rust-lang.org/tools/install))
### Build instructions
```sh
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.
## Usage example
### Rust
```rust
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](https://docs.rs/rubbler).
### C/C++
This example is a snippet taken from [rubbler-verilator-example]().
```c++
#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](https://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`.
## Integrating with Verilator
In the following discussion, we assume you have installed `verilator`, and the `verilator` command is available. See the [verilator doccumentation](https://veripool.org/guide/latest/index.html) for instructions on doing so. Both of the following example is taken from [rubbler-verilator-example]().
### Using Makefile
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`.
```makefile
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](https://github.com/fuad1502/rubbler-verilator-example). for the complete example.
### Using CMake `ExternalProject`
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
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](https://github.com/fuad1502/rubbler-verilator-example). for the complete example.
## Binary installation
To use the `rubble` binary system wide, install it with the following command:
```sh
cargo install rubbler
```
## Issues
- Using symbols inside memory addressing expressions is not yet supported.
## Planned features
- 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.