rvemu

RISC-V RV32I execution engine
git clone git://mcdim.xyz/revemu.git
Log | Files | Refs

commit c0766a7dce8dbe858706627a58ff370608787618
parent 4f7fe64aa5cbd2fb181c3fece01a0b806fc885a1
Author: Michail Konstantinos Dimopoulos <mk@mcdim.xyz>
Date:   Sat,  5 Sep 2026 00:25:13 +0300

Register regression-based testing

Diffstat:
MMakefile | 7+++++++
Dconfig.h | 2--
Mexec.c | 1+
Mmain.c | 51+++++++++++++++------------------------------------
Artests.c | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest.h | 31+++++++++++++++++++++++++++++++
6 files changed, 286 insertions(+), 38 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,3 +4,10 @@ all: -Wconversion -Wsign-conversion \ -Wshadow -Wstrict-prototypes \ -Wmissing-prototypes -Wformat=2 + +test: + gcc -g rtests.c exec.c disasm.c \ + -Wall -Wextra -Wpedantic \ + -Wconversion -Wsign-conversion \ + -Wshadow -Wstrict-prototypes \ + -Wmissing-prototypes -Wformat=2 diff --git a/config.h b/config.h @@ -1,2 +0,0 @@ -#define MEM_SIZE (32 * 1024 * 1024) - diff --git a/exec.c b/exec.c @@ -493,6 +493,7 @@ cpu_step(cpu_state_t *cpu, uint8_t *mem) if (!fn) { raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; } fn(cpu,inst,mem); diff --git a/main.c b/main.c @@ -19,23 +19,12 @@ load_program(uint8_t *mem) { /* program */ uint32_t arr[] = { -/* - 0x00500193, // addi x3, x0, 5 - 0x00600093, // addi x1, x0, 6 - 0xaaaaa3b7, // lui x7, 0xaaaaa -> rd:aaaaa000 - 0x00001397, // auipc x7 0x1 -> rd 1008 - 0x00000013, // nop - 0x0ff0000f, // fence - 0x00100073, // ebreak -*/ -/* 0xfff00093, // addi x1, x0, -1 0xfff04113, // xori x2, x0, -1 0xfff06193, // ori x3, x0, -1 0xfff17213, // andi x4, x2, -1 0xfff02293, // slti x5, x0, -1 0xfff03313 // sltiu x6, x0, -1 -*/ /* 0xfff00093, // li ra,-1 0xfff04113, // not sp,zero @@ -46,35 +35,13 @@ load_program(uint8_t *mem) 0x00100073 // ebreak */ /* -0x00a00093, //li ra,10 -0x00000113, //li sp,0 -0x00100193, //li gp,1 -0x00310133, //add sp,sp,gp -0x00118193, //addi gp,gp,1 -0xfff08093, //addi ra,ra,-1 -0xfe009ae3, //bnez ra,c <loop> -0x10000513, //li a0,256 -0x00252023, //sw sp,0(a0) -0x00350223, //sb gp,4(a0) -0x00052203, //lw tp,0(a0) -0x00450283, //lb t0,4(a0) -0x00121313, //slli t1,tp,0x1 -0x004343b3, //xor t2,t1,tp -0x03822413, //slti s0,tp,56 -0xfff23493, //sltiu s1,tp,-1 -0x00c005ef, //jal a1,4c <subroutine> -0x00c52423, //sw a2,8(a0) -0x00100073, //ebreak -0x00720613, //addi a2,tp,7 # 7 <_start+0x7> -0x00058067 //jr a1 -*/ - 0x00700513, // addi x10, x0, 7 0x00451513, // slli x10, x10, 4 0x00d50513, // addi x10, x10, 13 0x05554513, // xori x10, x10, 0x55 0xff750513, // addi x10, x10, -9 0x00100073 // ebreak +*/ /* 0x10000593, //li a1,256 0xfff00513, //li a0,-1 @@ -96,6 +63,18 @@ load_program(uint8_t *mem) 0x00000593, //li a1,0 0x00100073 //ebreak */ +/* + 0x00200093, // addi x1, x0, 2 + 0x00000113, // addi x2, x0, 0 + 0x05c00193, // addi x3, x0, 0x5c + 0x00110113, // addi x2, x2, 1 + 0x0021a023, // sw x2, 0(x3) + 0x00418193, // addi x3, x3, 4 + 0xfff08093, // addi x1, x1, -1 + 0xfe0098e3, // bne x1, x0, -16 + 0x00100073 // ebreak +*/ + }; size_t len = sizeof(arr) / sizeof(arr[0]); @@ -120,7 +99,6 @@ trap_handler(cpu_state_t *cpu) switch(cpu->trap.cause) { case TRAP_EBREAK: printf("Code reached breakpoint\n"); - return MODE_STEP; break; case TRAP_ECALL: printf("Code ECALL\n"); @@ -138,6 +116,7 @@ trap_handler(cpu_state_t *cpu) printf("No trap cause\n"); break; } + return MODE_STEP; } return MODE_RUN; } @@ -172,7 +151,7 @@ debugger(cpu_state_t *cpu, uint8_t *mem) } else if (strncmp(line, "m", 1) == 0 || strncmp(line, "memory", 6) == 0) { printf("Memory:\n"); - for (int i=0; i<128; i+=4) { + for (uint32_t i=0; i<128; i+=4) { if (cpu->pc == i) { printf("-> "); } else { diff --git a/rtests.c b/rtests.c @@ -0,0 +1,232 @@ +#include <stdint.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include "exec.h" +#include "mem.h" +#include "disasm.h" +#include "test.h" + +/**** TESTS ****/ + +test_t tests[] = { + { + .name = "upper immediate instructions", + .program = { + 0xaaaaa3b7, // lui x7, 0xaaaaa -> rd:aaaaa000 + 0x00001397, // auipc x7 0x1 -> rd 1004 + 0x00000013, // nop + 0x0ff0000f, // fence + 0x00100073, // ebreak + }, + .program_len = 5, + .exp = { + .x[7] = 0x1004, + .x_check_mask = (UINT32_C(1) << 7), + .pc = 16, + + }, + }, + { + .name = "negative I-type immediates", + .program = { + 0xfff00093, // addi x1, x0, -1 + 0xfff04113, // xori x2, x0, -1 + 0xfff06193, // ori x3, x0, -1 + 0xfff17213, // andi x4, x2, -1 + 0xfff02293, // slti x5, x0, -1 + 0xfff03313, // sltiu x6, x0, -1 + 0x00100073, // ebreak + }, + .program_len = 7, + .exp = { + .x = { + [1] = 0xffffffff, + [2] = 0xffffffff, + [3] = 0xffffffff, + [4] = 0xffffffff, + [5] = 0x0, + [6] = 0x1, + }, + .x_check_mask = (UINT32_C(1) << 6) | + (UINT32_C(1) << 5) | + (UINT32_C(1) << 4) | + (UINT32_C(1) << 3) | + (UINT32_C(1) << 2) | + (UINT32_C(1) << 1), + .pc = 0x18 + } + }, + { + .name = "I-type immediates", + .program = { + 0x00700513, // addi x10, x0, 7 + 0x00451513, // slli x10, x10, 4 + 0x00d50513, // addi x10, x10, 13 + 0x05554513, // xori x10, x10, 0x55 + 0xff750513, // addi x10, x10, -9 + 0x00100073 // ebreak + }, + .program_len = 6, + .exp = { + .x[10] = 0x0000001f, + .x_check_mask = (UINT32_C(1) << 10), + .pc = 0x14, + } + }, + { + .name = "basic loads and stores", + .program = { + 0x00a00093, //li ra,10 + 0x00000113, //li sp,0 + 0x00100193, //li gp,1 + 0x00310133, //add sp,sp,gp + 0x00118193, //addi gp,gp,1 + 0xfff08093, //addi ra,ra,-1 + 0xfe009ae3, //bnez ra,c <loop> + 0x10000513, //li a0,256 + 0x00252023, //sw sp,0(a0) + 0x00350223, //sb gp,4(a0) + 0x00052203, //lw tp,0(a0) + 0x00450283, //lb t0,4(a0) + 0x00121313, //slli t1,tp,0x1 + 0x004343b3, //xor t2,t1,tp + 0x03822413, //slti s0,tp,56 + 0xfff23493, //sltiu s1,tp,-1 + 0x00c005ef, //jal a1,4c <subroutine> + 0x00c52423, //sw a2,8(a0) + 0x00100073, //ebreak + 0x00720613, //addi a2,tp,7 # 7 <_start+0x7> + 0x00058067 //jr a1 + }, + .program_len = 21, + .exp = { + .x = { + [0] = 0x00000000, + [1] = 0x00000000, + [2] = 0x00000037, + [3] = 0x0000000b, + [4] = 0x00000037, + [5] = 0x0000000b, + [6] = 0x0000006e, + [7] = 0x00000059, + [8] = 0x00000001, + [9] = 0x00000001, + [10] = 0x00000100, + [11] = 0x00000044, + [12] = 0x0000003e, + + }, + .x_check_mask = ((UINT32_C(1) << 12) | + (UINT32_C(1) << 11) | + (UINT32_C(1) << 10) | + (UINT32_C(1) << 9) | + (UINT32_C(1) << 8) | + (UINT32_C(1) << 7) | + (UINT32_C(1) << 6) | + (UINT32_C(1) << 5) | + (UINT32_C(1) << 4) | + (UINT32_C(1) << 3) | + (UINT32_C(1) << 2) | + (UINT32_C(1) << 1) | + (UINT32_C(1))), + .pc = 0x48 + } + }, +}; + +static void +load_program(uint8_t *mem, uint32_t arr[], size_t len) +{ + int b = 0; /*byte*/ + for (size_t i=0; i<len; i++) { + + mem[b+0] = (uint8_t)(arr[i] >> 0); + mem[b+1] = (uint8_t)(arr[i] >> 8); + mem[b+2] = (uint8_t)(arr[i] >> 16); + mem[b+3] = (uint8_t)(arr[i] >> 24); + + b+=4; + } +} + +static void +print_trap(cpu_state_t *cpu) +{ + if (cpu->status == CPU_TRAPPED) { + switch(cpu->trap.cause) { + case TRAP_EBREAK: + printf("Code reached breakpoint\n"); + break; + case TRAP_ECALL: + printf("Code ECALL\n"); + break; + case TRAP_ILLEGAL_INSTRUCTION: + printf("Illegal instruction\n"); + break; + case TRAP_INSTRUCTION_MISALIGNED: + printf("Instruction misaligned\n"); + break; + case TRAP_INSTRUCTION_ACCESS_FAULT: + printf("Instruction access fault\n"); + break; + case TRAP_NONE: + printf("No trap cause\n"); + break; + } + } +} + +int +main() +{ + size_t n = sizeof(tests) / sizeof(test_t); + + int n_p = 0; + int n_f = 0; + for (size_t i=0; i<n; i++) { + + cpu_state_t cpu = {0}; + uint8_t mem[MEMSIZE] = {0}; + + load_program(mem, tests[i].program, tests[i].program_len); + + /* fetch & run */ + while (1) { + cpu_step(&cpu, mem); + if (cpu.status == CPU_TRAPPED) { + break; + } + } + + for (int j=0; j<32; j++) { + if ((UINT32_C(1) << j) == tests[i].exp.x_check_mask) { + if (cpu.x[j] != tests[i].exp.x[j]) { + tests[i].result = TEST_FAILURE; + printf( "bad register %d. Expected %x, got %x\n", + j, tests[i].exp.x[j], cpu.x[j]); + } + } + + if (cpu.pc != tests[i].exp.pc) { + tests[i].result = TEST_FAILURE; + puts("bad pc\n"); + } + } + if (tests[i].result != TEST_FAILURE) { + printf("ok - %s (%zu)\n", tests[i].name, i); + n_p++; + } else { + printf("not ok - %s (%zu)\n ", tests[i].name, i); + print_trap(&cpu); + n_f++; + } + + } + printf("\n%d passed, %d failed\n", n_p, n_f); + + return 0; +} diff --git a/test.h b/test.h @@ -0,0 +1,31 @@ +#ifndef TEST_H +#define TEST_H + +typedef enum { + TEST_SUCCESS, + TEST_FAILURE, +} test_res_t; + +typedef struct { + uint32_t expected; + uint32_t address; +} exp_word_t; + +typedef struct { + uint32_t x[32]; + uint32_t x_check_mask; + uint32_t pc; + + exp_word_t mem[50]; +} exp_t; + +typedef struct { + char *name; + uint32_t program[50]; + size_t program_len; + exp_t exp; + test_res_t result; +} test_t; + + +#endif