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:
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 {