Skip to content

Commit

Permalink
Merge pull request #2866 from o1-labs/dw/o1vm/riscv32im/unit-test-asm
Browse files Browse the repository at this point in the history
o1vm/riscv32im: adding programs for unit tests
  • Loading branch information
dannywillems authored Dec 11, 2024
2 parents b1d9f24 + 4c91348 commit 021346d
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ o1vm/op-program-db*
o1vm/state.json
meta.json
state.json

# Directory for the RISC-V 32bits toolchain
_riscv32-gnu-toolchain
50 changes: 44 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
# Known coverage limitations and issues:
# - https://github.com/rust-lang/rust/issues/79417
# - https://github.com/nextest-rs/nextest/issues/16
# FIXME: Update or remove the `codecov.yml` file to enable the `patch` coverage report and the corresponding PR check,
# once situation with the Rust's Doctests will be improved.
# FIXME: Update or remove the `codecov.yml` file to enable the `patch` coverage
# report and the corresponding PR check, once situation with the Rust's Doctests
# will be improved.
COVERAGE_ENV = CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS="-Cinstrument-coverage" LLVM_PROFILE_FILE=$(shell pwd)/target/profraw/cargo-test-%p-%m.profraw
# FIXME: In latest 0.8.19+ -t CLI argument can accept comma separated list of custom output types, hence, no need in double invocation
# FIXME: In latest 0.8.19+ -t CLI argument can accept comma separated list of
# custom output types, hence, no need in double invocation
GRCOV_CALL = grcov ./target/profraw --binary-path ./target/release/deps/ -s . --branch --ignore-not-existing --ignore "**/tests/**"
RISCV32_TOOLCHAIN_PATH = $(shell pwd)/_riscv32-gnu-toolchain

O1VM_RESOURCES_PATH = $(shell pwd)/o1vm/resources/programs
O1VM_RISCV32IM_SOURCE_DIR = ${O1VM_RESOURCES_PATH}/riscv32im/src
O1VM_RISCV32IM_SOURCE_FILES = $(wildcard ${O1VM_RISCV32IM_SOURCE_DIR}/*.S)
O1VM_RISCV32IM_BIN_DIR = ${O1VM_RESOURCES_PATH}/riscv32im/bin
O1VM_RISCV32IM_BIN_FILES = $(patsubst ${O1VM_RISCV32IM_SOURCE_DIR}/%.S,${O1VM_RISCV32IM_BIN_DIR}/%.o,${O1VM_RISCV32IM_SOURCE_FILES})
RISCV32_AS_FLAGS = --warn --fatal-warnings

# Default target
all: release
Expand Down Expand Up @@ -38,6 +48,7 @@ install-test-deps: ## Install test dependencies

clean: ## Clean the project
cargo clean
rm -rf $(O1VM_RISCV32IM_BIN_FILES)


build: ## Build the project
Expand Down Expand Up @@ -104,7 +115,7 @@ format: ## Format the code
lint: ## Lint the code
cargo clippy --all-features --all-targets --tests $(CARGO_EXTRA_ARGS) -- -W clippy::all -D warnings

generate-test-coverage-report:
generate-test-coverage-report: ## Generate the code coverage report
@echo ""
@echo "Generating the test coverage report."
@echo ""
Expand All @@ -118,7 +129,7 @@ generate-test-coverage-report:
@echo "The test coverage report is available at: ./target/coverage"
@echo ""

generate-doc:
generate-doc: ## Generate the Rust documentation
@echo ""
@echo "Generating the documentation."
@echo ""
Expand All @@ -131,4 +142,31 @@ help: ## Ask for help!
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'


.PHONY: all setup install-test-deps clean build release test-doc test-doc-with-coverage test test-with-coverage test-heavy test-heavy-with-coverage test-all test-all-with-coverage nextest nextest-with-coverage nextest-heavy nextest-heavy-with-coverage nextest-all nextest-all-with-coverage format lint generate-test-coverage-report generate-doc help
setup-riscv32-toolchain: ## Download and compile the RISC-V 32bits toolchain
@echo ""
@echo "Setting up the RISC-V 32-bit toolchain"
@echo ""
if [ ! -d $(RISCV32_TOOLCHAIN_PATH) ]; then \
git clone https://github.com/riscv-collab/riscv-gnu-toolchain ${RISCV32_TOOLCHAIN_PATH}; \
fi
cd ${RISCV32_TOOLCHAIN_PATH} && ./configure --with-arch=rv32gc --with-abi=ilp32d --prefix=${RISCV32_TOOLCHAIN_PATH}/build
cd ${RISCV32_TOOLCHAIN_PATH} && make -j 32 # require a good internet connection and some minutes
@echo ""
@echo "RISC-V 32-bits toolchain is ready in ${RISCV32_TOOLCHAIN_PATH}/build"
@echo ""

build-riscv32-programs: setup-riscv32-toolchain ${O1VM_RISCV32IM_BIN_FILES} ## Build all RISC-V 32 bits programs written for the o1vm

${O1VM_RISCV32IM_BIN_DIR}/%.o: ${O1VM_RISCV32IM_SOURCE_DIR}/%.S
@echo ""
@echo "Building the RISC-V 32-bits binary: $@ using $<"
@echo ""
mkdir -p ${O1VM_RISCV32IM_BIN_DIR}
${RISCV32_TOOLCHAIN_PATH}/build/bin/riscv32-unknown-elf-as ${RISCV32_AS_FLAGS} -o $@ $<
${RISCV32_TOOLCHAIN_PATH}/build/bin/riscv32-unknown-elf-ld -s -o $(basename $@) $@
@echo ""

fclean: clean ## Clean the tooling artefacts in addition to running clean
rm -rf ${RISCV32_TOOLCHAIN_PATH}

.PHONY: all setup install-test-deps clean build release test-doc test-doc-with-coverage test test-with-coverage test-heavy test-heavy-with-coverage test-all test-all-with-coverage nextest nextest-with-coverage nextest-heavy nextest-heavy-with-coverage nextest-all nextest-all-with-coverage format lint generate-test-coverage-report generate-doc setup-riscv32-toolchain help fclean build-riscv32-programs
42 changes: 42 additions & 0 deletions o1vm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,45 @@ RUN_WITH_CACHED_DATA="y" FILENAME="env-for-latest-l2-block.sh" O1VM_FLAVOR="pick
```shell
./clear-e2e-testing-cache.sh
```

## Running test programs

Different programs written either in Rust or directly in assembly are given in
the folder `resources/programs`. For each different architecture, you can see
examples.

As installing the toolchain for each ISA might not be easy on every development
platform, we do provide the source code and the corresponding assembly
respectively in `resources/programs/[ISA]/src` and
`resources/programs/[ISA]/bin`.

### RISC-V 32 bits (riscv32i, riscv32im)

For the RISC-V 32 bits architecture, the user can install the toolchain by using
`make setup-riscv32-toolchain`.

If you encounter any issue with the build dependencies, you can refer to [this
GitHub repository](https://github.com/riscv-collab/riscv-gnu-toolchain?tab=readme-ov-file#prerequisites).

The toolchain will be available in the directory
`_riscv32-gnu-toolchain/build` at the root of this repository (see variable
`RISCV32_TOOLCHAIN_PATH` in the [Makefile](../Makefile).

To compile on of the source files available in
`resources/programs/riscv32im/src`, the user can use:

```shell
FILENAME=sll.S

_riscv32-gnu-toolchain/build/bin/riscv32-unknown-elf-as
-o a.out \
o1vm/resources/programs/riscv32im/src/${FILENAME}
```

### Write new test examples

The Makefile at the top-level of this repository will automatically detect new
`.S` files in the directory `o1vm/resources/programs/riscv32im/src/` when the
target `build-riscv32-programs` is called. Any change to the existing files will
also be detected by the target, and you can commit the changes of the resulting
binary.
File renamed without changes.
File renamed without changes.
Binary file added o1vm/resources/programs/riscv32im/bin/sll
Binary file not shown.
17 changes: 17 additions & 0 deletions o1vm/resources/programs/riscv32im/src/sll.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.global _start

exit_success:
li a0, 0
li a1, 0
li a2, 0
li a3, 0
li a4, 0
li a5, 0
li a6, 0
li a7, 42
ecall

_start:
lui t0, 0x42
sll t0, t0, 2
jal exit_success
2 changes: 1 addition & 1 deletion o1vm/tests/test_elf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
fn test_correctly_parsing_elf() {
let curr_dir = std::env::current_dir().unwrap();
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32im/fibonacci",
"resources/programs/riscv32im/bin/fibonacci",
));
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();

Expand Down
23 changes: 21 additions & 2 deletions o1vm/tests/test_riscv_elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn test_instruction_can_be_converted_into_string() {
fn test_no_action() {
let curr_dir = std::env::current_dir().unwrap();
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32im/no-action",
"resources/programs/riscv32im/bin/no-action",
));
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();
let mut witness = Env::<Fp>::create(PAGE_SIZE.try_into().unwrap(), state);
Expand Down Expand Up @@ -56,7 +56,7 @@ fn test_no_action() {
fn test_fibonacci_7() {
let curr_dir = std::env::current_dir().unwrap();
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32im/fibonacci-7",
"resources/programs/riscv32im/bin/fibonacci-7",
));
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();
let mut witness = Env::<Fp>::create(PAGE_SIZE.try_into().unwrap(), state);
Expand All @@ -73,3 +73,22 @@ fn test_fibonacci_7() {
}
}
}

// FIXME: stop ignore when all the instructions necessary for running this
// program are implemented.
#[test]
#[ignore]
fn test_sll() {
let curr_dir = std::env::current_dir().unwrap();
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32im/bin/sll",
));
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();
let mut witness = Env::<Fp>::create(PAGE_SIZE.try_into().unwrap(), state);

while !witness.halt {
witness.step();
}

// FIXME: check the state of the registers after the program has run.
}

0 comments on commit 021346d

Please sign in to comment.