Skip to content

Commit 7f74eaf

Browse files
Refactor RV32I code, add RV32I tests, update Makefile, cleanup code (#8)
* Refactor RV32I code, add RV32I tests, update Makefile, cleanup code Notable changes: - Replaced `riscv32i` with `rv32i` in function names to standardise the formatting - Split syscall handling into helper functions - Refactored memory r/w functions - Added `test_riscv32i.c` for basic test cases - Removed deprecated test cases in `test_utils.c` - Update `.gitignore` to include files for the future LaTeX paper * Fix xpm setup in `make-ci.yml` workflow, update flags for debugging in same workflow * TEMP: Add debugging steps to check all possible locations of toolchain * FINAL: Minify workflow
1 parent 5898e86 commit 7f74eaf

10 files changed

Lines changed: 339 additions & 284 deletions

File tree

.github/workflows/make-ci.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,26 @@ jobs:
1313

1414
steps:
1515
- uses: actions/checkout@v5
16-
- name: make
17-
run: make
18-
- name: make test
16+
- name: Setup Node.js for xpm
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: '20'
20+
- name: Install xpm and riscv-none-elf toolchain
21+
shell: bash
22+
run: |
23+
set -euox pipefail
24+
npm install --location=global xpm@latest
25+
mkdir -p .ci-xpm
26+
printf '%s\n' '{' \
27+
' "name": "vbo-ci-xpm",' \
28+
' "version": "0.0.0",' \
29+
' "private": true,' \
30+
' "xpack": {}' \
31+
'}' > .ci-xpm/package.json
32+
( cd .ci-xpm && xpm install @xpack-dev-tools/[email protected] --verbose )
33+
TOOLCHAIN_BIN="$HOME/.local/xPacks/@xpack-dev-tools/riscv-none-elf-gcc/14.2.0-3.1/.content/bin"
34+
echo "$TOOLCHAIN_BIN" >> "$GITHUB_PATH"
35+
echo "RV_PREFIX=riscv-none-elf-" >> "$GITHUB_ENV"
36+
"$TOOLCHAIN_BIN/riscv-none-elf-gcc" --version
37+
- name: make test # test incorporates build into it
1938
run: make test

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,10 @@ test/**/*
7171

7272
*.bin
7373
*.app
74-
*.img
74+
*.img
75+
76+
# latex
77+
paper/**/*
78+
!paper/**/*.tex
79+
!paper/**/*.bib
80+
!paper/**/*.pdf

Makefile

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SRC_MAIN = src/main.c $(SRC_COMMON)
1111

1212
FLAGS = -Wall -Wextra -Werror -g -std=c11 -pedantic
1313

14-
# RISC-V cross toolchain (see docs/compiler.md)
14+
# RISC-V cross toolchain (see the wiki)
1515
RV_PREFIX ?= ~/.local/xPacks/riscv-none-elf-gcc/xpack-riscv-none-elf-gcc-14.2.0-3/bin/riscv-none-elf-
1616
RV_GCC := $(RV_PREFIX)gcc
1717
RV_OBJCOPY:= $(RV_PREFIX)objcopy
@@ -27,7 +27,7 @@ ORIGIN ?= 0x3000
2727

2828
define ensure_rv_toolchain
2929
@if ! command -v $(RV_GCC) >/dev/null 2>&1; then \
30-
echo "[Make] Missing toolchain: $(RV_GCC). See docs/compiler.md to install and add to PATH."; \
30+
echo "[Make] Missing toolchain: $(RV_GCC). See the wiki for info on how to install and add to PATH."; \
3131
exit 1; \
3232
fi
3333
@if ! command -v $(RV_OBJCOPY) >/dev/null 2>&1; then \
@@ -44,7 +44,7 @@ endif
4444

4545
TEST_DIR = test
4646

47-
.PHONY: all build clean test distclean app image embed run demo clean-app test-integration test-all help
47+
.PHONY: all build clean test app image embed run demo distclean test-integration test-all help
4848

4949
all: build
5050

@@ -61,16 +61,26 @@ clean:
6161
rm -f $(TEST_DIR)/test_utils
6262
rm -f images/vbo_image.o
6363
rm -rf $(OUT_DIR)
64-
6564
distclean: clean
6665

67-
test:
66+
test: build
6867
$(CC) $(INC) $(TEST_DIR)/test_utils.c src/utils.c src/riscv32i.c -o $(TEST_DIR)/test_utils $(FLAGS)
6968
$(TEST_DIR)/test_utils
7069
rm $(TEST_DIR)/test_utils
7170

72-
# Run both unit and integration tests (integration is skipped if toolchain is missing)
73-
test-all: test test-integration
71+
$(CC) $(INC) $(TEST_DIR)/test_riscv32i.c src/riscv32i.c -o $(TEST_DIR)/test_riscv32i $(FLAGS)
72+
$(TEST_DIR)/test_riscv32i
73+
rm $(TEST_DIR)/test_riscv32i
74+
75+
@if ! command -v $(RV_GCC) >/dev/null 2>&1 || ! command -v $(RV_OBJCOPY) >/dev/null 2>&1; then \
76+
echo "[Test] Skipping integration test (toolchain not found). See the wiki"; \
77+
exit 0; \
78+
fi
79+
@$(MAKE) --no-print-directory image
80+
@out=$$($(BIN) $(APP_IMG)); echo "$$out" | grep -q "Hello from RV32I VM" && echo "OK" || (echo "FAIL"; exit 1)
81+
82+
83+
7484

7585
# --- App build pipeline ---
7686

@@ -104,19 +114,6 @@ run: build image
104114

105115
demo: embed run
106116

107-
clean-app:
108-
rm -rf $(OUT_DIR)
109-
110-
# Integration test (optional): requires toolchain, builds hello and checks output
111-
test-integration: build
112-
@if ! command -v $(RV_GCC) >/dev/null 2>&1 || ! command -v $(RV_OBJCOPY) >/dev/null 2>&1; then \
113-
echo "[Test] Skipping integration test (toolchain not found). See docs/compiler.md"; \
114-
exit 0; \
115-
fi
116-
@$(MAKE) --no-print-directory image
117-
@echo "[Test] Running integration test (hello)"
118-
@out=$$($(BIN) $(APP_IMG)); echo "$$out" | grep -q "Hello from RV32I VM" && echo "[Test] OK" || (echo "[Test] FAIL"; exit 1)
119-
120117
help:
121118
@echo "Targets:"
122119
@echo " build - Build the VM"

include/riscv32i.h

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ static inline int32_t rv32i_sext(uint32_t val, int bits) {
6969

7070

7171
// define instructions
72-
void exec_riscv32i(uint32_t instruction);
72+
void exec_rv32i(uint32_t instruction);
7373

7474
// functions for all instructions, in the format:
75-
// void exec_riscv32i_<mnemonic>(uint32_t instruction);
75+
// void exec_rv32i_<mnemonic>(uint32_t instruction);
7676

7777
/* canonical order:
7878
addi, slti[u], andi, ori, xori, slli, srli, srai, lui, auipc, // integer register-immediate
@@ -171,46 +171,46 @@ enum
171171

172172

173173
// integer register-immediate
174-
void exec_riscv32i_addi(uint32_t instruction);
175-
void exec_riscv32i_slti(uint32_t instruction);
176-
void exec_riscv32i_andi(uint32_t instruction);
177-
void exec_riscv32i_ori(uint32_t instruction);
178-
void exec_riscv32i_xori(uint32_t instruction);
179-
void exec_riscv32i_slli(uint32_t instruction);
180-
void exec_riscv32i_srli_srai(uint32_t instruction);
181-
void exec_riscv32i_lui(uint32_t instruction);
182-
void exec_riscv32i_auipc(uint32_t instruction);
183-
void exec_riscv32i_sltiu(uint32_t instruction);
174+
void exec_rv32i_addi(uint32_t instruction);
175+
void exec_rv32i_slti(uint32_t instruction);
176+
void exec_rv32i_andi(uint32_t instruction);
177+
void exec_rv32i_ori(uint32_t instruction);
178+
void exec_rv32i_xori(uint32_t instruction);
179+
void exec_rv32i_slli(uint32_t instruction);
180+
void exec_rv32i_srli_srai(uint32_t instruction);
181+
void exec_rv32i_lui(uint32_t instruction);
182+
void exec_rv32i_auipc(uint32_t instruction);
183+
void exec_rv32i_sltiu(uint32_t instruction);
184184
// integer register-register
185-
void exec_riscv32i_add_sub(uint32_t instruction);
186-
void exec_riscv32i_slt(uint32_t instruction);
187-
void exec_riscv32i_sltu(uint32_t instruction);
188-
void exec_riscv32i_and(uint32_t instruction);
189-
void exec_riscv32i_or(uint32_t instruction);
190-
void exec_riscv32i_xor(uint32_t instruction);
191-
void exec_riscv32i_sll(uint32_t instruction);
192-
void exec_riscv32i_srl_sra(uint32_t instruction);
185+
void exec_rv32i_add_sub(uint32_t instruction);
186+
void exec_rv32i_slt(uint32_t instruction);
187+
void exec_rv32i_sltu(uint32_t instruction);
188+
void exec_rv32i_and(uint32_t instruction);
189+
void exec_rv32i_or(uint32_t instruction);
190+
void exec_rv32i_xor(uint32_t instruction);
191+
void exec_rv32i_sll(uint32_t instruction);
192+
void exec_rv32i_srl_sra(uint32_t instruction);
193193
// unconditional jumps
194-
void exec_riscv32i_jal(uint32_t instruction);
195-
void exec_riscv32i_jalr(uint32_t instruction);
194+
void exec_rv32i_jal(uint32_t instruction);
195+
void exec_rv32i_jalr(uint32_t instruction);
196196
// conditional branches
197-
void exec_riscv32i_beq(uint32_t instruction);
198-
void exec_riscv32i_bne(uint32_t instruction);
199-
void exec_riscv32i_blt(uint32_t instruction);
200-
void exec_riscv32i_bge(uint32_t instruction);
201-
void exec_riscv32i_bltu(uint32_t instruction);
202-
void exec_riscv32i_bgeu(uint32_t instruction);
197+
void exec_rv32i_beq(uint32_t instruction);
198+
void exec_rv32i_bne(uint32_t instruction);
199+
void exec_rv32i_blt(uint32_t instruction);
200+
void exec_rv32i_bge(uint32_t instruction);
201+
void exec_rv32i_bltu(uint32_t instruction);
202+
void exec_rv32i_bgeu(uint32_t instruction);
203203
// load and store
204-
void exec_riscv32i_load(uint32_t instruction);
205-
void exec_riscv32i_store(uint32_t instruction);
204+
void exec_rv32i_load(uint32_t instruction);
205+
void exec_rv32i_store(uint32_t instruction);
206206
// memory ordering
207-
void exec_riscv32i_fence(uint32_t instruction);
207+
void exec_rv32i_fence(uint32_t instruction);
208208
// environment call and breakpoints
209-
void exec_riscv32i_system(uint32_t instruction);
209+
void exec_rv32i_system(uint32_t instruction);
210210

211211

212212
// error handling
213-
void exec_riscv32i_bad_opcode(uint32_t instruction);
213+
void exec_rv32i_bad_opcode(uint32_t instruction);
214214

215215
//
216216

include/rv32_syscalls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// Each wrapper issues an ecall with a7=syscall number and returns a0.
44
// On error, the return value is a negative errno (Linux convention).
55

6+
// ? for a real-world use, it'd be much more efficient to implement a c library (newlib?)
7+
68
#ifndef RV32_SYSCALLS_H
79
#define RV32_SYSCALLS_H
810

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ int main(int argc, const char* argv[])
7373
break;
7474
}
7575
rv32i_reg[pc] += 4; // advance to next 32-bit instruction
76-
exec_riscv32i(instr);
76+
exec_rv32i(instr);
7777
}
7878
return 0;
7979
}

0 commit comments

Comments
 (0)