#risc-v #assembly #line #instructions #bench #cpu #verilator

bin+lib 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.

2 releases

0.1.2 Mar 5, 2024
0.1.1 Mar 5, 2024
0.1.0 Mar 5, 2024

#117 in Profiling

MIT/Apache

205KB
5.5K SLoC

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.

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 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 for the syntax of each directives.

Building from source

Requirements

Rust (see installing Rust)

Build instructions

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

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.

C/C++

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.

Integrating with Verilator

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.

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.

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.

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_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.

Binary installation

To use the rubble binary system wide, install it with the following command:

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.

No runtime deps

~0–1.3MB
~19K SLoC