rvemu

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

commit f03a0c9fd688ec40d0268660b221e742efc40a35
parent 4f6ec22e8cb9e98e786ab20e830f52cf1042e019
Author: Michail Konstantinos Dimopoulos <mk@mcdim.xyz>
Date:   Mon,  7 Sep 2026 16:39:01 +0300

Added compiled C Fibonacci test, fixed S-type sign

Diffstat:
MMakefile | 6+++---
Mdecode.h | 9+++++++--
Mexec.c | 2+-
Mexec.h | 4++++
Mrtests.c | 1065++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest.h | 1+
6 files changed, 1078 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile @@ -18,8 +18,8 @@ CFLAGS= -lreadline \ -Wshadow -Wstrict-prototypes \ -Wmissing-prototypes -Wformat=2 -emu: exec.c exec.h disasm.c disasm.h trap.h mem.h main.c +emu: exec.c exec.h disasm.c disasm.h trap.h mem.h decode.h main.c $(CC) $(CPPFLAGS) $(CFLAGS) main.c exec.c disasm.c $(LFLAGS) -o $@ -test: exec.c exec.h disasm.c disasm.h trap.h mem.h rtests.c test.h - $(CC) $(CFLAGS) rtests.c exec.c disasm.c -o $@ +test: exec.c exec.h disasm.c disasm.h trap.h mem.h decode.h rtests.c test.h + $(CC) -DTEST_BUILD $(CFLAGS) rtests.c exec.c disasm.c -o $@ diff --git a/decode.h b/decode.h @@ -88,7 +88,7 @@ opcode 0x33 0100011 typedef struct { uint8_t opcode; uint8_t rs1, rs2; - uint16_t imm; + int32_t imm; uint8_t funct3; } stype_t; @@ -97,7 +97,12 @@ decode_stype(stype_t *s, uint32_t inst) { uint32_t imm_11_5 = (inst >> 25) & 127; uint32_t imm_4_0 = (inst >> 7) & 31; - s->imm = (uint16_t)((imm_4_0) | (imm_11_5 << 5)); + uint32_t raw = (uint16_t)((imm_4_0) | (imm_11_5 << 5)); + + s->imm = (int32_t)raw; + + if ((raw & UINT32_C(0x800)) != 0u) + s->imm -= INT32_C(0x1000); s->funct3 = (inst >> 12) & 7; s->rs1 = (inst >> 15) & 31; diff --git a/exec.c b/exec.c @@ -293,7 +293,7 @@ static void exec_stype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { stype_t s; decode_stype(&s, inst); - uint32_t addr = s.imm + cpu->x[s.rs1]; + uint32_t addr = (uint32_t)s.imm + cpu->x[s.rs1]; /* TODO: helpers, better checks */ switch(s.funct3) { diff --git a/exec.h b/exec.h @@ -3,7 +3,11 @@ #include "trap.h" +#ifdef TEST_BUILD #define MEMSIZE 4096 +#else +#define MEMSIZE 65536 +#endif typedef enum { CPU_RUNNING = 0, diff --git a/rtests.c b/rtests.c @@ -27,7 +27,7 @@ test_t tests[] = { .x[7] = 0x1004, .x_check_mask = (UINT32_C(1) << 7), .pc = 16, - + .cause = TRAP_EBREAK, }, }, { @@ -57,7 +57,8 @@ test_t tests[] = { (UINT32_C(1) << 3) | (UINT32_C(1) << 2) | (UINT32_C(1) << 1), - .pc = 0x18 + .pc = 0x18, + .cause = TRAP_EBREAK, } }, { @@ -75,6 +76,7 @@ test_t tests[] = { .x[10] = 0x0000001f, .x_check_mask = (UINT32_C(1) << 10), .pc = 0x14, + .cause = TRAP_EBREAK, } }, { @@ -133,7 +135,8 @@ test_t tests[] = { (UINT32_C(1) << 2) | (UINT32_C(1) << 1) | (UINT32_C(1))), - .pc = 0x48 + .pc = 0x48, + .cause = TRAP_EBREAK, } }, { @@ -168,6 +171,7 @@ test_t tests[] = { .x_check_mask = ((UINT32_C(1) << 10) | (UINT32_C(1) << 11)), .pc = 0x48, + .cause = TRAP_EBREAK, .mem = { { .address = 0x100, .expected = 0xff }, { .address = 0x101, .expected = 0x00 }, @@ -218,8 +222,1036 @@ test_t tests[] = { (UINT32_C(1) << 10) | (UINT32_C(1) << 28)), .pc = 0x2c, + .cause = TRAP_EBREAK, + } + }, + { + .name = "load/store edge values", + .program = { + 0x20000a13, // addi x20,x0,0x200 + 0x00001ab7, // lui x21,0x1 + 0xffca8a93, // addi x21,x21,-4 -> 0xffc + 0xfff00093, // addi x1,x0,-1 -> 0xffffffff + 0x07f00113, // addi x2,x0,0x7f + 0x08000193, // addi x3,x0,0x80 + 0x00008237, // lui x4,0x8 -> 0x8000 + 0xfff20213, // addi x4,x4,-1 -> 0x7fff + 0x000082b7, // lui x5,0x8 -> 0x8000 + 0x12345337, // lui x6,0x12345 + 0x67830313, // addi x6,x6,0x678 -> 0x12345678 + 0x002a0023, // sb x2,0(x20) -> 0x7f + 0x003a00a3, // sb x3,1(x20) -> 0x80 + 0x004a1123, // sh x4,2(x20) -> 0x7fff + 0x005a1223, // sh x5,4(x20) -> 0x8000 + 0x001a2423, // sw x1,8(x20) -> 0xffffffff + 0x006a2623, // sw x6,12(x20) -> 0x12345678 + 0x000a2823, // sw x0,16(x20) -> 0 + 0x006aa023, // sw x6,0(x21) -> mem[0xffc] + 0x000a0403, // lb x8,0(x20) -> 0x0000007f + 0x001a0483, // lb x9,1(x20) -> 0xffffff80 + 0x001a4503, // lbu x10,1(x20) -> 0x00000080 + 0x002a1583, // lh x11,2(x20) -> 0x00007fff + 0x004a1603, // lh x12,4(x20) -> 0xffff8000 + 0x004a5683, // lhu x13,4(x20) -> 0x00008000 + 0x008a2703, // lw x14,8(x20) -> 0xffffffff + 0x00ca2783, // lw x15,12(x20) -> 0x12345678 + 0x000aa803, // lw x16,0(x21) -> 0x12345678 + 0x010a2883, // lw x17,16(x20) -> 0 + 0x00100073, // ebreak + }, + .program_len = 30, + .exp = { + .x = { + [1] = 0xffffffff, + [2] = 0x0000007f, + [3] = 0x00000080, + [4] = 0x00007fff, + [5] = 0x00008000, + [6] = 0x12345678, + [8] = 0x0000007f, + [9] = 0xffffff80, + [10] = 0x00000080, + [11] = 0x00007fff, + [12] = 0xffff8000, + [13] = 0x00008000, + [14] = 0xffffffff, + [15] = 0x12345678, + [16] = 0x12345678, + [17] = 0x00000000, + [20] = 0x00000200, + [21] = 0x00000ffc, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 2) | + (UINT32_C(1) << 3) | + (UINT32_C(1) << 4) | + (UINT32_C(1) << 5) | + (UINT32_C(1) << 6) | + (UINT32_C(1) << 8) | + (UINT32_C(1) << 9) | + (UINT32_C(1) << 10) | + (UINT32_C(1) << 11) | + (UINT32_C(1) << 12) | + (UINT32_C(1) << 13) | + (UINT32_C(1) << 14) | + (UINT32_C(1) << 15) | + (UINT32_C(1) << 16) | + (UINT32_C(1) << 17) | + (UINT32_C(1) << 20) | + (UINT32_C(1) << 21), + .pc = 0x74, + .cause = TRAP_EBREAK, + .mem = { + { .address = 0x200, .expected = 0x7f }, + { .address = 0x201, .expected = 0x80 }, + { .address = 0x202, .expected = 0xff }, + { .address = 0x203, .expected = 0x7f }, + { .address = 0x204, .expected = 0x00 }, + { .address = 0x205, .expected = 0x80 }, + { .address = 0x208, .expected = 0xff }, + { .address = 0x209, .expected = 0xff }, + { .address = 0x20a, .expected = 0xff }, + { .address = 0x20b, .expected = 0xff }, + { .address = 0x20c, .expected = 0x78 }, + { .address = 0x20d, .expected = 0x56 }, + { .address = 0x20e, .expected = 0x34 }, + { .address = 0x20f, .expected = 0x12 }, + { .address = 0x210, .expected = 0x00 }, + { .address = 0x211, .expected = 0x00 }, + { .address = 0x212, .expected = 0x00 }, + { .address = 0x213, .expected = 0x00 }, + { .address = 0xffc, .expected = 0x78 }, + { .address = 0xffd, .expected = 0x56 }, + { .address = 0xffe, .expected = 0x34 }, + { .address = 0xfff, .expected = 0x12 }, + }, + .mem_len = 22, + } + }, + { + .name = "branches and jumps", + .program = { + /* Operands */ + 0x00500093, // addi x1,x0,5 + 0x00500113, // addi x2,x0,5 + 0xfff00193, // addi x3,x0,-1 + 0x00100213, // addi x4,x0,1 + + /* BEQ taken */ + 0x00100513, // addi x10,x0,1 + 0x00208463, // beq x1,x2,+8 + 0x00000513, // addi x10,x0,0 skipped + + /* BEQ not taken */ + 0x00408463, // beq x1,x4,+8 + 0x00100593, // addi x11,x0,1 + + /* BNE taken */ + 0x00100613, // addi x12,x0,1 + 0x00409463, // bne x1,x4,+8 + 0x00000613, // addi x12,x0,0 skipped + + /* BNE not taken */ + 0x00209463, // bne x1,x2,+8 + 0x00100693, // addi x13,x0,1 + + /* BLT taken: -1 < 1 */ + 0x00100713, // addi x14,x0,1 + 0x0041c463, // blt x3,x4,+8 + 0x00000713, // addi x14,x0,0 skipped + + /* BLT not taken: 1 < -1 is false */ + 0x00324463, // blt x4,x3,+8 + 0x00100793, // addi x15,x0,1 + + /* BGE taken: 1 >= -1 */ + 0x00100813, // addi x16,x0,1 + 0x00325463, // bge x4,x3,+8 + 0x00000813, // addi x16,x0,0 skipped + + /* BGE not taken: -1 >= 1 is false */ + 0x0041d463, // bge x3,x4,+8 + 0x00100893, // addi x17,x0,1 + + /* BLTU taken: 1 < 5 */ + 0x00100913, // addi x18,x0,1 + 0x00126463, // bltu x4,x1,+8 + 0x00000913, // addi x18,x0,0 skipped + + /* BLTU not taken: 0xffffffff < 1 is false */ + 0x0041e463, // bltu x3,x4,+8 + 0x00100993, // addi x19,x0,1 + + /* BGEU taken: 0xffffffff >= 1 */ + 0x00100a13, // addi x20,x0,1 + 0x0041f463, // bgeu x3,x4,+8 + 0x00000a13, // addi x20,x0,0 skipped + + /* BGEU not taken: 1 >= 0xffffffff is false */ + 0x00327463, // bgeu x4,x3,+8 + 0x00100a93, // addi x21,x0,1 + + /* + * JAL at 0x8c: + * link x22 = 0x90 + * target = 0x94 + */ + 0x00100b93, // addi x23,x0,1 + 0x00800b6f, // jal x22,+8 + 0x00000b93, // addi x23,x0,0 skipped + + /* + * Construct the odd address 0xa9. + * JALR must clear bit zero and jump to 0xa8. + */ + 0x00000c17, // auipc x24,0 -> 0x94 + 0x015c0c13, // addi x24,x24,21 -> 0xa9 + 0x00100c93, // addi x25,x0,1 + 0x000c0d67, // jalr x26,0(x24) + 0x00000c93, // addi x25,x0,0 skipped + + 0x00100073, // ebreak at 0xa8 + }, + .program_len = 43, + .exp = { + .x = { + [1] = 0x00000005, + [2] = 0x00000005, + [3] = 0xffffffff, + [4] = 0x00000001, + + [10] = 0x00000001, // BEQ taken + [11] = 0x00000001, // BEQ not taken + [12] = 0x00000001, // BNE taken + [13] = 0x00000001, // BNE not taken + [14] = 0x00000001, // BLT taken + [15] = 0x00000001, // BLT not taken + [16] = 0x00000001, // BGE taken + [17] = 0x00000001, // BGE not taken + [18] = 0x00000001, // BLTU taken + [19] = 0x00000001, // BLTU not taken + [20] = 0x00000001, // BGEU taken + [21] = 0x00000001, // BGEU not taken + + [22] = 0x00000090, // JAL return address + [23] = 0x00000001, // JAL skipped failure + [24] = 0x000000a9, // odd JALR base + [25] = 0x00000001, // JALR skipped failure + [26] = 0x000000a4, // JALR return address + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 2) | + (UINT32_C(1) << 3) | + (UINT32_C(1) << 4) | + (UINT32_C(0x1ffff) << 10), + + .pc = 0xa8, + .cause = TRAP_EBREAK, + } + }, + { + .name = "R-type arithmetic edge cases", + .program = { + 0xfff00093, // addi x1,x0,-1 + 0x00100113, // addi x2,x0,1 + 0x01f00193, // addi x3,x0,31 + 0x80000237, // lui x4,0x80000 -> INT32_MIN + 0x800002b7, // lui x5,0x80000 + 0xfff28293, // addi x5,x5,-1 -> INT32_MAX + + 0x00228533, // add x10,x5,x2 -> 0x80000000 + 0x402005b3, // sub x11,x0,x2 -> 0xffffffff + 0x00311633, // sll x12,x2,x3 -> 0x80000000 + 0x002226b3, // slt x13,x4,x2 -> 1 + 0x00223733, // sltu x14,x4,x2 -> 0 + 0x0050c7b3, // xor x15,x1,x5 -> 0x80000000 + 0x00225833, // srl x16,x4,x2 -> 0x40000000 + 0x402258b3, // sra x17,x4,x2 -> 0xc0000000 + 0x00526933, // or x18,x4,x5 -> 0xffffffff + 0x005279b3, // and x19,x4,x5 -> 0x00000000 + + 0x00111a33, // sll x20,x2,x1 -> shift by 31 + 0x00125ab3, // srl x21,x4,x1 -> shift by 31 + 0x40125b33, // sra x22,x4,x1 -> shift by 31 + + 0x00208033, // add x0,x1,x2 -> x0 must remain zero + 0x40220bb3, // sub x23,x4,x2 -> 0x7fffffff + 0x00100073, // ebreak + }, + .program_len = 22, + .exp = { + .x = { + [0] = 0x00000000, + [1] = 0xffffffff, + [2] = 0x00000001, + [3] = 0x0000001f, + [4] = 0x80000000, + [5] = 0x7fffffff, + + [10] = 0x80000000, + [11] = 0xffffffff, + [12] = 0x80000000, + [13] = 0x00000001, + [14] = 0x00000000, + [15] = 0x80000000, + [16] = 0x40000000, + [17] = 0xc0000000, + [18] = 0xffffffff, + [19] = 0x00000000, + [20] = 0x80000000, + [21] = 0x00000001, + [22] = 0xffffffff, + [23] = 0x7fffffff, + }, + .x_check_mask = + UINT32_C(0x3f) | + (UINT32_C(0x3fff) << 10), + + .pc = 0x54, + .cause = TRAP_EBREAK, + } + }, + { + .name = "JAL misaligned target", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x002002ef, // jal x5,+2 + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_INSTRUCTION_MISALIGNED, + } + }, + { + .name = "JAL target above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000012ef, // jal x5,+4096 + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "JAL target below memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0xff9ff2ef, // jal x5,-8: 0x4 - 8 + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "JALR misaligned target", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x00200093, // addi x1,x0,2 + 0x000082e7, // jalr x5,0(x1): target 0x2 + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00000002, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_MISALIGNED, + } + }, + { + .name = "JALR target above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1: 0x1000 + 0x000082e7, // jalr x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00001000, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "JALR wrapped negative target", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x00000093, // addi x1,x0,0 + 0xffc082e7, // jalr x5,-4(x1) + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00000000, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "illegal JALR funct3", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000012e7, // Illegal JALR, funct3=1 + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "taken branch misaligned target", + .program = { + 0x00100093, // addi x1,x0,1 + 0x05500293, // addi x5,x0,0x55 + 0x00108163, // beq x1,x1,+2 + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00000001, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_MISALIGNED, + } + }, + { + .name = "taken branch target above memory", + .program = { + 0x00100093, // addi x1,x0,1 + 0x05500293, // addi x5,x0,0x55 + 0x7e108ee3, // beq x1,x1,+4092 + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00000001, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "taken branch target below memory", + .program = { + 0x00100093, // addi x1,x0,1 + 0x05500293, // addi x5,x0,0x55 + 0xfe108ae3, // beq x1,x1,-12: 0x8 - 12 + }, + .program_len = 3, + .exp = { + .x = { + [1] = 0x00000001, + [5] = 0x00000055, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_INSTRUCTION_ACCESS_FAULT, + } + }, + { + .name = "illegal branch funct3 2", + .program = { + 0x00002063, // branch opcode with reserved funct3=2 + }, + .program_len = 1, + .exp = { + .pc = 0x0, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "illegal branch funct3 3", + .program = { + 0x00003063, // branch opcode with reserved funct3=3 + }, + .program_len = 1, + .exp = { + .pc = 0x0, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "not-taken branch ignores misaligned target", + .program = { + 0x00100093, // addi x1,x0,1 + 0x00200113, // addi x2,x0,2 + 0x00208163, // beq x1,x2,+2: false, target would be 0xa + 0x00100193, // addi x3,x0,1: must execute + 0x00100073, // ebreak + }, + .program_len = 5, + .exp = { + .x = { + [3] = 0x00000001, + }, + .x_check_mask = (UINT32_C(1) << 3), + .pc = 0x10, + .cause = TRAP_EBREAK, // use your EBREAK cause name + } + }, + { + .name = "LH misaligned address", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x00100093, // addi x1,x0,1 + 0x00009283, // lh x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_MISALIGNED, + } + }, + { + .name = "LHU misaligned address", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x00100093, // addi x1,x0,1 + 0x0000d283, // lhu x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_MISALIGNED, + } + }, + { + .name = "LW misaligned address", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x00200093, // addi x1,x0,2 + 0x0000a283, // lw x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_MISALIGNED, + } + }, + { + .name = "SH misaligned address", + .program = { + 0x20000093, // addi x1,x0,0x200 + 0x05500293, // addi x5,x0,0x55 + 0x005090a3, // sh x5,1(x1) + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_STORE_MISALIGNED, + .mem = { + { .address = 0x201, .expected = 0x00 }, + { .address = 0x202, .expected = 0x00 }, + }, + .mem_len = 2, + } + }, + { + .name = "SW misaligned address", + .program = { + 0x20000093, // addi x1,x0,0x200 + 0x05500293, // addi x5,x0,0x55 + 0x0050a123, // sw x5,2(x1) + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_STORE_MISALIGNED, + .mem = { + { .address = 0x202, .expected = 0x00 }, + { .address = 0x203, .expected = 0x00 }, + { .address = 0x204, .expected = 0x00 }, + { .address = 0x205, .expected = 0x00 }, + }, + .mem_len = 4, + } + }, + { + .name = "LB address above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x00008283, // lb x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_ACCESS_FAULT, + } + }, + { + .name = "LBU address above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x0000c283, // lbu x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_ACCESS_FAULT, } }, + { + .name = "LH address above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x00009283, // lh x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_ACCESS_FAULT, + } + }, + { + .name = "LHU address above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x0000d283, // lhu x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_ACCESS_FAULT, + } + }, + { + .name = "LW address above memory", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x0000a283, // lw x5,0(x1) + }, + .program_len = 3, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x8, + .cause = TRAP_LOAD_ACCESS_FAULT, + } + }, + { + .name = "SB address above memory", + .program = { + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x05500293, // addi x5,x0,0x55 + 0x00508023, // sb x5,0(x1) + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_STORE_ACCESS_FAULT, + } + }, + { + .name = "SH address above memory", + .program = { + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x05500293, // addi x5,x0,0x55 + 0x00509023, // sh x5,0(x1) + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_STORE_ACCESS_FAULT, + } + }, + { + .name = "SW address above memory", + .program = { + 0x000010b7, // lui x1,0x1 -> 0x1000 + 0x05500293, // addi x5,x0,0x55 + 0x0050a023, // sw x5,0(x1) + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_STORE_ACCESS_FAULT, + } + }, + { + .name = "load wrapped negative address", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0xfff00283, // lb x5,-1(x0) -> 0xffffffff + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_LOAD_ACCESS_FAULT, + } + }, + { + .name = "I-type arithmetic edge cases", + .program = { + 0x800000b7, // lui x1,0x80000 + 0xfff08093, // addi x1,x1,-1 -> INT32_MAX + 0x80000137, // lui x2,0x80000 -> INT32_MIN + + 0x00108513, // addi x10,x1,1 -> 0x80000000 + 0xfff10593, // addi x11,x2,-1 -> 0x7fffffff + 0xfff12613, // slti x12,x2,-1 -> 1 + 0xfff0a693, // slti x13,x1,-1 -> 0 + 0xfff03713, // sltiu x14,x0,-1 -> 1 + 0xfff13793, // sltiu x15,x2,-1 -> 1 + 0xfff04813, // xori x16,x0,-1 -> 0xffffffff + 0x80006893, // ori x17,x0,-2048 -> 0xfffff800 + 0x55587913, // andi x18,x16,0x555 -> 0x00000555 + 0xfff17993, // andi x19,x2,-1 -> 0x80000000 + + 0x00100073, // ebreak + }, + .program_len = 14, + .exp = { + .x = { + [1] = 0x7fffffff, + [2] = 0x80000000, + + [10] = 0x80000000, + [11] = 0x7fffffff, + [12] = 0x00000001, + [13] = 0x00000000, + [14] = 0x00000001, + [15] = 0x00000001, + [16] = 0xffffffff, + [17] = 0xfffff800, + [18] = 0x00000555, + [19] = 0x80000000, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 2) | + (UINT32_C(0x3ff) << 10), + + .pc = 0x34, + .cause = TRAP_EBREAK, + } + }, + { + .name = "I-type shift boundary amounts", + .program = { + 0x00100093, // addi x1,x0,1 + 0x80000137, // lui x2,0x80000 + + 0x00009513, // slli x10,x1,0 -> 0x00000001 + 0x01f09593, // slli x11,x1,31 -> 0x80000000 + 0x00015613, // srli x12,x2,0 -> 0x80000000 + 0x01f15693, // srli x13,x2,31 -> 0x00000001 + 0x40115713, // srai x14,x2,1 -> 0xc0000000 + 0x41f15793, // srai x15,x2,31 -> 0xffffffff + + 0x00100073, // ebreak + }, + .program_len = 9, + .exp = { + .x = { + [1] = 0x00000001, + [2] = 0x80000000, + [10] = 0x00000001, + [11] = 0x80000000, + [12] = 0x80000000, + [13] = 0x00000001, + [14] = 0xc0000000, + [15] = 0xffffffff, + }, + .x_check_mask = + (UINT32_C(1) << 1) | + (UINT32_C(1) << 2) | + (UINT32_C(0x3f) << 10), + + .pc = 0x20, + .cause = TRAP_EBREAK, + } + }, + { + .name = "illegal SLLI upper immediate bits", + .program = { + 0x00100093, // addi x1,x0,1 + 0x05500113, // addi x2,x0,0x55 + 0x02009113, // illegal SLLI: imm[11:5]=0x01 + }, + .program_len = 3, + .exp = { + .x = { + [2] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 2), + .pc = 0x8, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "illegal right-shift immediate encoding", + .program = { + 0x800000b7, // lui x1,0x80000 + 0x05500113, // addi x2,x0,0x55 + 0x0200d113, // illegal SRLI/SRAI: imm[11:5]=0x01 + }, + .program_len = 3, + .exp = { + .x = { + [2] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 2), + .pc = 0x8, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "LUI and AUIPC at nonzero PC", + .program = { + 0x00000013, // nop + 0x00000013, // nop + 0x12345297, // auipc x5,0x12345 -> 0x12345008 + 0x12345337, // lui x6,0x12345 -> 0x12345000 + 0x00100073, // ebreak + }, + .program_len = 5, + .exp = { + .x = { + [5] = 0x12345008, + [6] = 0x12345000, + }, + .x_check_mask = + (UINT32_C(1) << 5) | + (UINT32_C(1) << 6), + + .pc = 0x10, + .cause = TRAP_EBREAK, + } + }, + { + .name = "negative load and store offsets", + .program = { + 0x20400093, // addi x1,x0,0x204 + 0x12300113, // addi x2,x0,0x123 + 0xfe20ae23, // sw x2,-4(x1) -> address 0x200 + 0xffc0a183, // lw x3,-4(x1) + 0x00100073, // ebreak + }, + .program_len = 5, + .exp = { + .x = { + [2] = 0x00000123, + [3] = 0x00000123, + }, + .x_check_mask = + (UINT32_C(1) << 2) | + (UINT32_C(1) << 3), + + .pc = 0x10, + .cause = TRAP_EBREAK, + + .mem = { + { .address = 0x200, .expected = 0x23 }, + { .address = 0x201, .expected = 0x01 }, + { .address = 0x202, .expected = 0x00 }, + { .address = 0x203, .expected = 0x00 }, + }, + .mem_len = 4, + } + }, + { + .name = "illegal R-type funct7", + .program = { + 0x00100093, // addi x1,x0,1 + 0x00200113, // addi x2,x0,2 + 0x05500293, // addi x5,x0,0x55 + 0x022082b3, // illegal ADD-like instruction: funct7=1 + }, + .program_len = 4, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0xc, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "illegal load funct3", + .program = { + 0x05500293, // addi x5,x0,0x55 + 0x0000b283, // load opcode with reserved funct3=3 + }, + .program_len = 2, + .exp = { + .x = { + [5] = 0x00000055, + }, + .x_check_mask = (UINT32_C(1) << 5), + .pc = 0x4, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "illegal store funct3", + .program = { + 0x20000093, // addi x1,x0,0x200 + 0x05500293, // addi x5,x0,0x55 + 0x0050b023, // store opcode with reserved funct3=3 + }, + .program_len = 3, + .exp = { + .pc = 0x8, + .cause = TRAP_ILLEGAL_INSTRUCTION, + .mem = { + { .address = 0x200, .expected = 0x00 }, + { .address = 0x201, .expected = 0x00 }, + { .address = 0x202, .expected = 0x00 }, + { .address = 0x203, .expected = 0x00 }, + }, + .mem_len = 4, + } + }, + { + .name = "illegal SYSTEM instruction", + .program = { + 0x00200073, // neither ECALL nor EBREAK + }, + .program_len = 1, + .exp = { + .pc = 0x0, + .cause = TRAP_ILLEGAL_INSTRUCTION, + } + }, + { + .name = "Compiled freestanding C Fibonacci", + .program = { + 0x00001137, //lui sp,0x1 + 0x008000ef, //jal c <main> + 0x00100073, //ebreak + 0xfe010113, //addi sp,sp,-32 # fe0 <main+0xfd4> + 0x00112e23, //sw ra,28(sp) + 0x00812c23, //sw s0,24(sp) + 0x02010413, //addi s0,sp,32 + 0xfe042623, //sw zero,-20(s0) + 0x00100793, //li a5,1 + 0xfef42423, //sw a5,-24(s0) + 0xfe042223, //sw zero,-28(s0) + 0x0300006f, //j 5c <main+0x50> + 0xfec42703, //lw a4,-20(s0) + 0xfe842783, //lw a5,-24(s0) + 0x00f707b3, //add a5,a4,a5 + 0xfef42023, //sw a5,-32(s0) + 0xfe842783, //lw a5,-24(s0) + 0xfef42623, //sw a5,-20(s0) + 0xfe042783, //lw a5,-32(s0) + 0xfef42423, //sw a5,-24(s0) + 0xfe442783, //lw a5,-28(s0) + 0x00178793, //addi a5,a5,1 + 0xfef42223, //sw a5,-28(s0) + 0xfe442703, //lw a4,-28(s0) + 0x01300793, //li a5,19 + 0xfce7d6e3, //bge a5,a4,30 <main+0x24> + 0xfec42783, //lw a5,-20(s0) + 0x00078513, //mv a0,a5 + 0x01c12083, //lw ra,28(sp) + 0x01812403, //lw s0,24(sp) + 0x02010113, //addi sp,sp,32 + 0x00008067, //ret + }, + .program_len = 32, + .exp = { + .pc = 0x8, + .cause = TRAP_EBREAK, + .x = { + [1] = 0x8, + [2] = 0x1000, + [10] = 0x1a6d, /* F(20) = 6765 */ + }, + .x_check_mask = (UINT32_C(1) | + (UINT32_C(1) << 2) | + (UINT32_C(1) << 10)), + }, + }, }; static void @@ -257,6 +1289,18 @@ print_trap(cpu_state_t *cpu) case TRAP_INSTRUCTION_ACCESS_FAULT: printf("Instruction access fault\n"); break; + case TRAP_LOAD_MISALIGNED: + printf("Load misaligned\n"); + break; + case TRAP_LOAD_ACCESS_FAULT: + printf("Load access fault\n"); + break; + case TRAP_STORE_MISALIGNED: + printf("Store misaligned\n"); + break; + case TRAP_STORE_ACCESS_FAULT: + printf("Store access fault\n"); + break; case TRAP_NONE: printf("No trap cause\n"); break; @@ -307,6 +1351,17 @@ check_pc(uint32_t pc, size_t i, int pr) } } +static void +check_cause(cpu_state_t *cpu, size_t i, int pr) +{ + if (cpu->trap.cause != tests[i].exp.cause) { + tests[i].result = TEST_FAILURE; + if (pr) + printf( " bad cause. Expected %x, got %x\n", + tests[i].exp.cause, cpu->trap.cause); + } +} + int main() { @@ -338,6 +1393,9 @@ main() /* check pc */ check_pc(cpu.pc, i, 0); + /* check trap cause */ + check_cause(&cpu, i, 0); + if (tests[i].result != TEST_FAILURE) { printf("ok - %s (%zu)\n", tests[i].name, i); n_p++; @@ -348,6 +1406,7 @@ main() check_reg(&cpu, i, 1); check_mem(mem, i, 1); check_pc(cpu.pc, i, 1); + check_cause(&cpu, i, 1); n_f++; } diff --git a/test.h b/test.h @@ -18,6 +18,7 @@ typedef struct { exp_word_t mem[50]; int mem_len; + trap_cause_t cause; } exp_t; typedef struct {