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:
| M | Makefile | | | 7 | +++++++ |
| D | config.h | | | 2 | -- |
| M | exec.c | | | 1 | + |
| M | main.c | | | 51 | +++++++++++++++------------------------------------ |
| A | rtests.c | | | 232 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | test.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