rvemu

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

commit 2cc43400b091dc7d16b7d8880ba4e2d47f2702ad
parent 4e256a9cea6be3e08d06d1c63ab90e9cd4dd1824
Author: Michail Konstantinos Dimopoulos <mk@mcdim.xyz>
Date:   Fri,  4 Sep 2026 18:04:00 +0300

Separated execution from driver

Diffstat:
MMakefile | 2+-
Mdisasm.c | 4++--
Aexec.c | 512+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexec.h | 23+++++++++++++++++++++++
Mmain.c | 667++++++++++++-------------------------------------------------------------------
5 files changed, 635 insertions(+), 573 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ all: - gcc -g main.c disasm.c \ + gcc -g main.c exec.c disasm.c \ -Wall -Wextra -Wpedantic \ -Wconversion -Wsign-conversion \ -Wshadow -Wstrict-prototypes \ diff --git a/disasm.c b/disasm.c @@ -138,7 +138,7 @@ static void disasm_itype_loads(uint32_t inst) { return; } - printf(" x%" PRIu32 ", %" PRId32 "(x%" PRIu32 ")", + printf(" x%" PRIu32 ", %" PRId32 "(x%" PRIu32 ")\n", i.rd, i.imm, i.rs1); } @@ -161,7 +161,7 @@ static void disasm_stype(uint32_t inst) { return; } - printf(" x%" PRIu32 ", %" PRId32 "(x%" PRIu32 ")", + printf(" x%" PRIu32 ", %" PRId32 "(x%" PRIu32 ")\n", s.rs2, (int32_t)(int16_t)s.imm, s.rs1); } diff --git a/exec.c b/exec.c @@ -0,0 +1,512 @@ +#include <stdint.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "exec.h" +#include "decode.h" +#include "disasm.h" +#include "trap.h" + + +static inline void print_bin16(uint16_t x) { + for (int i = 15; i >= 0; i--) { + putchar((x >> i) & 1 ? '1' : '0'); + /*if (i % 4 == 0) putchar(' '); grouping */ + } +} + +static inline void print_bin32(uint32_t x) { + for (int i = 31; i >= 0; i--) { + putchar((x >> i) & 1 ? '1' : '0'); + } +} + +static inline void print_binless(uint32_t x, int a) { + for (int i = a-1; i >= 0; i--) { + putchar((x >> i) & 1 ? '1' : '0'); + } +} + +static void +raise_trap(cpu_state_t *cpu, trap_cause_t trapinfo, uint32_t tval) +{ + cpu->status = CPU_TRAPPED; + cpu->trap.cause = trapinfo; + cpu->trap.pc = cpu->pc; + cpu->trap.tval = tval; + return; +} + +static inline trap_cause_t +validate_instruction_address(int64_t addr) +{ + if (addr < 0 || addr > (int64_t)MEMSIZE - INT64_C(4)) + return TRAP_INSTRUCTION_ACCESS_FAULT; + + if ((addr & INT64_C(3)) != 0) + return TRAP_INSTRUCTION_MISALIGNED; + + return TRAP_NONE; +} + +static trap_cause_t +get_valid_branch_target(uint32_t pc, int32_t offset, uint32_t *target) +{ + int64_t candidate = (int64_t)pc + (int64_t)offset; + + trap_cause_t valid = validate_instruction_address(candidate); + if (valid != TRAP_NONE) + return valid; + + *target = (uint32_t)candidate; + return valid; +} + +static trap_cause_t +get_valid_jalr_target(uint32_t base, int32_t offset, uint32_t *target) +{ + int64_t candidate = ((int64_t)base + (int64_t)offset) + & ~INT64_C(1); + trap_cause_t valid = validate_instruction_address(candidate); + if (valid != TRAP_NONE) + return valid; + + *target = (uint32_t)candidate; + return valid; +} + +static void exec_itype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + (void)mem; /* suppressing unused parameters warning */ + itype_t i; + decode_itype(&i, inst); + + uint32_t x; uint32_t shamt; + uint32_t uimm = (uint32_t)i.imm; + switch(i.funct3) { + case 0x0: + x = cpu->x[i.rs1]+uimm; + break; + case 0x1: + if (i.funct7 == 0x00) { + shamt = i.imm & 31; + x = cpu->x[i.rs1] << shamt; + } else { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + break; + case 0x2: + x = ((int32_t)cpu->x[i.rs1] < (int32_t)i.imm) ? 1 : 0; + break; + case 0x3: + x = (cpu->x[i.rs1] < uimm) ? 1 : 0; + break; + case 0x4: + x = cpu->x[i.rs1]^uimm; + break; + case 0x5: + shamt = i.imm & 31; + switch(i.funct7) { + case 0x00: + x = cpu->x[i.rs1] >> shamt; + break; + case 0x20: + x = (uint32_t)((int32_t)cpu->x[i.rs1] >> shamt); + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + break; + case 0x6: + x = cpu->x[i.rs1]|uimm; + break; + case 0x7: + x = cpu->x[i.rs1]&uimm; + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + if (i.rd != 0) { + cpu->x[i.rd] = x; + } + + cpu->pc += 4; +} + +static void exec_rtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + (void)mem; /* suppressing unused parameters warning */ + rtype_t r; + decode_rtype(&r, inst); + + uint32_t x; + uint32_t shamt; + switch(r.funct3) { + case 0x0: + switch(r.funct7) { + case 0x00: + x = cpu->x[r.rs1]+cpu->x[r.rs2]; + break; + case 0x20: + x = cpu->x[r.rs1]-cpu->x[r.rs2]; + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + break; + case 0x1: + shamt = cpu->x[r.rs2] & 31; + x = cpu->x[r.rs1] << shamt; + break; + case 0x2: + x = ((int32_t)cpu->x[r.rs1] < (int32_t)cpu->x[r.rs2]) ? 1 : 0; + break; + case 0x3: + x = (cpu->x[r.rs1] < cpu->x[r.rs2]) ? 1 : 0; + break; + case 0x4: + x = cpu->x[r.rs1]^cpu->x[r.rs2]; + break; + case 0x5: + switch(r.funct7) { + case 0x00: + shamt = cpu->x[r.rs2] & 31; + x = cpu->x[r.rs1] >> shamt; + break; + case 0x20: + shamt = cpu->x[r.rs2] & 31; + x = (uint32_t)((int32_t)cpu->x[r.rs1] >> shamt); + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + break; + case 0x6: + x = cpu->x[r.rs1]|cpu->x[r.rs2]; + break; + case 0x7: + x = cpu->x[r.rs1]&cpu->x[r.rs2]; + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + if (r.rd != 0) { + cpu->x[r.rd] = x; + } + + cpu->pc += 4; +} + +static void exec_itype_loads(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + itype_t i; + decode_itype(&i, inst); + + uint32_t addr = (uint32_t)i.imm + cpu->x[i.rs1]; + + /* TODO: do I need to check before loading halves and words */ + + uint32_t x; + switch(i.funct3) { + case 0x0: + x = (uint32_t)(int32_t)(int8_t)mem[addr]; + break; + case 0x1: + uint16_t half = (uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8); + x = (uint32_t)(int32_t)(int16_t)half; + break; + case 0x2: + /* TODO: clean up */ + x = (uint32_t)(int32_t)( + ((uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8)) + | + (((uint16_t)mem[addr+2] | ((uint16_t)mem[addr+3] << 8)) + << 16) + ); + break; + case 0x4: + x = (uint32_t)(uint8_t)mem[addr]; + break; + + case 0x5: + int16_t uhalf = (int16_t)mem[addr] | ((int16_t)mem[addr+1] << 8); + x = (uint32_t)(int32_t)uhalf; + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + if (i.rd != 0) { + cpu->x[i.rd] = x; + } + cpu->pc += 4; +} + +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]; + + /* TODO: helpers, better checks */ + switch(s.funct3) { + case 0x0: + mem[addr] = cpu->x[s.rs2] & 255; + break; + case 0x1: + if (addr+1 > MEMSIZE-1) { + fprintf(stderr, "Illegal addr\n"); + return; + } + + if (addr % 2 == 0) { + mem[addr] = (uint8_t)cpu->x[s.rs2]; + mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; + } + break; + case 0x2: + if (addr+3 > MEMSIZE-1) { + fprintf(stderr, "Illegal addr\n"); + return; + } + if (addr % 4 == 0) { + mem[addr] = (uint8_t)cpu->x[s.rs2]; + mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; + mem[addr+2] = (uint8_t)cpu->x[s.rs2] >> 16; + mem[addr+3] = (uint8_t)cpu->x[s.rs2] >> 24; + } + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + cpu->pc += 4; +} + +static void exec_btype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + (void)mem; /* suppressing unused parameters warning */ + btype_t b; + decode_btype(&b, inst); + + int jump = 0; + switch (b.funct3) { + case 0x0: + if (cpu->x[b.rs1] == cpu->x[b.rs2]) + jump = 1; + break; + case 0x1: + if (cpu->x[b.rs1] != cpu->x[b.rs2]) + jump = 1; + break; + case 0x4: + if ((int32_t)cpu->x[b.rs1] < (int32_t)cpu->x[b.rs2]) + jump = 1; + break; + case 0x5: + if ((int32_t)cpu->x[b.rs1] >= (int32_t)cpu->x[b.rs2]) + jump = 1; + break; + case 0x6: + if (cpu->x[b.rs1] < cpu->x[b.rs2]) + jump = 1; + break; + case 0x7: + if (cpu->x[b.rs1] >= cpu->x[b.rs2]) + jump = 1; + break; + default: + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + if (jump == 1) { + uint32_t target; + trap_cause_t cause = get_valid_branch_target(cpu->pc, + b.imm, + &target); + if (cause != TRAP_NONE) { + raise_trap(cpu, cause, inst); + return; + } + cpu->pc = target; + } else { + cpu->pc += 4; + } + +} + +static void exec_itype_jalr(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + (void)mem; /* suppressing unused parameters warning */ + itype_t i; + decode_itype(&i, inst); + + uint32_t x; + if (i.funct3 != 0x0) { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + x = cpu->pc + 4; + + uint32_t target; + trap_cause_t cause = get_valid_jalr_target(cpu->x[i.rs1], i.imm, + &target); + if (cause != TRAP_NONE) { + raise_trap(cpu, cause, inst); + return; + } + + cpu->pc = target; + + if (i.rd != 0) { + cpu->x[i.rd] = x; + } +} + +static void exec_jtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { + (void)mem; /* suppressing unused parameters warning */ + jtype_t j; + decode_jtype(&j, inst); + + uint32_t x; + x = cpu->pc + 4; + cpu->pc += j.imm; + + uint32_t target; + trap_cause_t cause = get_valid_branch_target(cpu->pc, + (int32_t)j.imm, + &target); + if (cause != TRAP_NONE) { + raise_trap(cpu, cause, inst); + return; + } + + cpu->pc = target; + + if (j.rd != 0) { + cpu->x[j.rd] = x; + } +} + +static void +exec_utype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) +{ + (void)mem; /* suppressing unused parameters warning */ + utype_t u; + decode_utype(&u, inst); + + if (u.rd != 0) + cpu->x[u.rd] = u.imm << 12; + + cpu->pc += 4; +} + +static void +exec_utype_auipc(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) +{ + (void)mem; /* suppressing unused parameters warning */ + utype_t u; + decode_utype(&u, inst); + + if (u.rd != 0) + cpu->x[u.rd] = cpu->pc + (u.imm << 12); + + cpu->pc += 4; +} + +static void +exec_itype_control(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) +{ + (void)mem; /* suppressing unused parameters warning */ + itype_t i; + decode_itype(&i, inst); + + if (i.funct3 != 0x0) { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + if (i.imm == 0x0) { + raise_trap(cpu, TRAP_ECALL, inst); + return; + } else if (i.imm == 0x1) { + raise_trap(cpu, TRAP_EBREAK, inst); + return; + } else { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + +} + +static void +exec_itype_fence(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) +{ + (void)mem; /* suppressing unused parameters warning */ + itype_t i; + decode_itype(&i, inst); + + if (i.funct3 != 0x0) { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + return; + } + + cpu->pc += 4; +} + +static inline uint32_t +make_instruction(uint8_t *mem, uint32_t index) +{ + uint32_t inst = 0; + inst |= (uint32_t)(mem[index+0]) << 0; + inst |= (uint32_t)(mem[index+1]) << 8; + inst |= (uint32_t)(mem[index+2]) << 16; + inst |= (uint32_t)(mem[index+3]) << 24; + return inst; + +} + +/* Function pointer dispatch */ +typedef void (*exec_fn)(cpu_state_t *, uint32_t inst, uint8_t *mem); + +static exec_fn opcode_table[128] = { + [0x33] = exec_rtype, + [0x13] = exec_itype, + [0x03] = exec_itype_loads, + [0x67] = exec_itype_jalr, + [0x73] = exec_itype_control, + [0x0f] = exec_itype_fence, + [0x23] = exec_stype, + [0x63] = exec_btype, + [0x6f] = exec_jtype, + [0x37] = exec_utype, + [0x17] = exec_utype_auipc +}; +/***/ + +void +cpu_step(cpu_state_t *cpu, uint8_t *mem) +{ + uint32_t inst = make_instruction(mem, cpu->pc); + disassemble_instruction(inst); + + /* decode opcode */ + uint16_t opcode = inst & 127; + + exec_fn fn = opcode_table[opcode]; + + if (!fn) { + raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); + } + + fn(cpu,inst,mem); +} diff --git a/exec.h b/exec.h @@ -0,0 +1,23 @@ +#ifndef EXEC_H +#define EXEC_H + +#include "trap.h" + +#define MEMSIZE 2052 + +typedef enum { + CPU_RUNNING = 0, + CPU_TRAPPED, + CPU_HALTED +} cpu_status_t; + +typedef struct { + uint32_t x[32]; /* general purpose registers */ + uint32_t pc; + trap_info_t trap; + cpu_status_t status; +} cpu_state_t; + +void cpu_step(cpu_state_t *cpu, uint8_t *mem); + +#endif diff --git a/main.c b/main.c @@ -4,482 +4,13 @@ #include <stdlib.h> #include <string.h> -#include "decode.h" #include "disasm.h" -#include "trap.h" - -#define MEMSIZE 2052 - -typedef enum { - CPU_RUNNING = 0, - CPU_TRAPPED, - CPU_HALTED -} cpu_status_t; - -typedef struct { - uint32_t x[32]; /* general purpose registers */ - uint32_t pc; - trap_info_t trap; - cpu_status_t status; -} cpu_state_t; - -static inline void print_bin16(uint16_t x) { - for (int i = 15; i >= 0; i--) { - putchar((x >> i) & 1 ? '1' : '0'); - /*if (i % 4 == 0) putchar(' '); grouping */ - } -} - -static inline void print_bin32(uint32_t x) { - for (int i = 31; i >= 0; i--) { - putchar((x >> i) & 1 ? '1' : '0'); - } -} - -static inline void print_binless(uint32_t x, int a) { - for (int i = a-1; i >= 0; i--) { - putchar((x >> i) & 1 ? '1' : '0'); - } -} - -static void -raise_trap(cpu_state_t *cpu, trap_cause_t trapinfo, uint32_t tval) -{ - cpu->status = CPU_TRAPPED; - cpu->trap.cause = trapinfo; - cpu->trap.pc = cpu->pc; - cpu->trap.tval = tval; - return; -} - -static inline trap_cause_t -validate_instruction_address(int64_t addr) -{ - if (addr < 0 || addr > (int64_t)MEMSIZE - INT64_C(4)) - return TRAP_INSTRUCTION_ACCESS_FAULT; - - if ((addr & INT64_C(3)) != 0) - return TRAP_INSTRUCTION_MISALIGNED; - - return TRAP_NONE; -} - -static trap_cause_t -get_valid_branch_target(uint32_t pc, int32_t offset, uint32_t *target) -{ - int64_t candidate = (int64_t)pc + (int64_t)offset; - - trap_cause_t valid = validate_instruction_address(candidate); - if (valid != TRAP_NONE) - return valid; - - *target = (uint32_t)candidate; - return valid; -} - -static trap_cause_t -get_valid_jalr_target(uint32_t base, int32_t offset, uint32_t *target) -{ - int64_t candidate = ((int64_t)base + (int64_t)offset) - & ~INT64_C(1); - trap_cause_t valid = validate_instruction_address(candidate); - if (valid != TRAP_NONE) - return valid; - - *target = (uint32_t)candidate; - return valid; -} - -static void exec_itype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - (void)mem; /* suppressing unused parameters warning */ - itype_t i; - decode_itype(&i, inst); - - uint32_t x; uint32_t shamt; - uint32_t uimm = (uint32_t)i.imm; - switch(i.funct3) { - case 0x0: - x = cpu->x[i.rs1]+uimm; - break; - case 0x1: - if (i.funct7 == 0x00) { - shamt = i.imm & 31; - x = cpu->x[i.rs1] << shamt; - } else { - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - break; - case 0x2: - x = ((int32_t)cpu->x[i.rs1] < (int32_t)i.imm) ? 1 : 0; - break; - case 0x3: - x = (cpu->x[i.rs1] < uimm) ? 1 : 0; - break; - case 0x4: - x = cpu->x[i.rs1]^uimm; - break; - case 0x5: - shamt = i.imm & 31; - switch(i.funct7) { - case 0x00: - x = cpu->x[i.rs1] >> shamt; - break; - case 0x20: - x = (uint32_t)((int32_t)cpu->x[i.rs1] >> shamt); - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - break; - case 0x6: - x = cpu->x[i.rs1]|uimm; - break; - case 0x7: - x = cpu->x[i.rs1]&uimm; - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - if (i.rd != 0) { - cpu->x[i.rd] = x; - } - - cpu->pc += 4; -} - -static void exec_rtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - (void)mem; /* suppressing unused parameters warning */ - rtype_t r; - decode_rtype(&r, inst); - - uint32_t x; - uint32_t shamt; - switch(r.funct3) { - case 0x0: - switch(r.funct7) { - case 0x00: - x = cpu->x[r.rs1]+cpu->x[r.rs2]; - break; - case 0x20: - x = cpu->x[r.rs1]-cpu->x[r.rs2]; - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - break; - case 0x1: - shamt = cpu->x[r.rs2] & 31; - x = cpu->x[r.rs1] << shamt; - break; - case 0x2: - x = ((int32_t)cpu->x[r.rs1] < (int32_t)cpu->x[r.rs2]) ? 1 : 0; - break; - case 0x3: - x = (cpu->x[r.rs1] < cpu->x[r.rs2]) ? 1 : 0; - break; - case 0x4: - x = cpu->x[r.rs1]^cpu->x[r.rs2]; - break; - case 0x5: - switch(r.funct7) { - case 0x00: - shamt = cpu->x[r.rs2] & 31; - x = cpu->x[r.rs1] >> shamt; - break; - case 0x20: - shamt = cpu->x[r.rs2] & 31; - x = (uint32_t)((int32_t)cpu->x[r.rs1] >> shamt); - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - break; - case 0x6: - x = cpu->x[r.rs1]|cpu->x[r.rs2]; - break; - case 0x7: - x = cpu->x[r.rs1]&cpu->x[r.rs2]; - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - if (r.rd != 0) { - cpu->x[r.rd] = x; - } - - cpu->pc += 4; -} - -static void exec_itype_loads(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - itype_t i; - decode_itype(&i, inst); - - uint32_t addr = (uint32_t)i.imm + cpu->x[i.rs1]; - - /* TODO: do I need to check before loading halves and words */ - - uint32_t x; - switch(i.funct3) { - case 0x0: - x = (uint32_t)(int32_t)(int8_t)mem[addr]; - break; - case 0x1: - uint16_t half = (uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8); - x = (uint32_t)(int32_t)(int16_t)half; - break; - case 0x2: - /* TODO: clean up */ - x = (uint32_t)(int32_t)( - ((uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8)) - | - (((uint16_t)mem[addr+2] | ((uint16_t)mem[addr+3] << 8)) - << 16) - ); - break; - case 0x4: - x = (uint32_t)(uint8_t)mem[addr]; - break; - - case 0x5: - int16_t uhalf = (int16_t)mem[addr] | ((int16_t)mem[addr+1] << 8); - x = (uint32_t)(int32_t)uhalf; - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - if (i.rd != 0) { - cpu->x[i.rd] = x; - } - cpu->pc += 4; -} - -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]; - - /* TODO: helpers, better checks */ - switch(s.funct3) { - case 0x0: - mem[addr] = cpu->x[s.rs2] & 255; - break; - case 0x1: - if (addr+1 > MEMSIZE-1) { - fprintf(stderr, "Illegal addr\n"); - return; - } - - if (addr % 2 == 0) { - mem[addr] = (uint8_t)cpu->x[s.rs2]; - mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; - } - break; - case 0x2: - if (addr+3 > MEMSIZE-1) { - fprintf(stderr, "Illegal addr\n"); - return; - } - if (addr % 4 == 0) { - mem[addr] = (uint8_t)cpu->x[s.rs2]; - mem[addr+1] = (uint8_t)cpu->x[s.rs2] >> 8; - mem[addr+2] = (uint8_t)cpu->x[s.rs2] >> 16; - mem[addr+3] = (uint8_t)cpu->x[s.rs2] >> 24; - } - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - cpu->pc += 4; -} - -static void exec_btype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - (void)mem; /* suppressing unused parameters warning */ - btype_t b; - decode_btype(&b, inst); - - int jump = 0; - switch (b.funct3) { - case 0x0: - if (cpu->x[b.rs1] == cpu->x[b.rs2]) - jump = 1; - break; - case 0x1: - if (cpu->x[b.rs1] != cpu->x[b.rs2]) - jump = 1; - break; - case 0x4: - if ((int32_t)cpu->x[b.rs1] < (int32_t)cpu->x[b.rs2]) - jump = 1; - break; - case 0x5: - if ((int32_t)cpu->x[b.rs1] >= (int32_t)cpu->x[b.rs2]) - jump = 1; - break; - case 0x6: - if (cpu->x[b.rs1] < cpu->x[b.rs2]) - jump = 1; - break; - case 0x7: - if (cpu->x[b.rs1] >= cpu->x[b.rs2]) - jump = 1; - break; - default: - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - if (jump == 1) { - uint32_t target; - trap_cause_t cause = get_valid_branch_target(cpu->pc, - b.imm, - &target); - if (cause != TRAP_NONE) { - raise_trap(cpu, cause, inst); - return; - } - cpu->pc = target; - } else { - cpu->pc += 4; - } - -} - -static void exec_itype_jalr(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - (void)mem; /* suppressing unused parameters warning */ - itype_t i; - decode_itype(&i, inst); - - uint32_t x; - if (i.funct3 != 0x0) { - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - x = cpu->pc + 4; - - uint32_t target; - trap_cause_t cause = get_valid_jalr_target(cpu->x[i.rs1], i.imm, - &target); - if (cause != TRAP_NONE) { - raise_trap(cpu, cause, inst); - return; - } - - cpu->pc = target; - - if (i.rd != 0) { - cpu->x[i.rd] = x; - } -} - -static void exec_jtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) { - (void)mem; /* suppressing unused parameters warning */ - jtype_t j; - decode_jtype(&j, inst); - - uint32_t x; - x = cpu->pc + 4; - cpu->pc += j.imm; - - uint32_t target; - trap_cause_t cause = get_valid_branch_target(cpu->pc, - (int32_t)j.imm, - &target); - if (cause != TRAP_NONE) { - raise_trap(cpu, cause, inst); - return; - } - - cpu->pc = target; - - if (j.rd != 0) { - cpu->x[j.rd] = x; - } -} - -static void -exec_utype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) -{ - (void)mem; /* suppressing unused parameters warning */ - utype_t u; - decode_utype(&u, inst); - - if (u.rd != 0) - cpu->x[u.rd] = u.imm << 12; - - cpu->pc += 4; -} - -static void -exec_utype_auipc(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) -{ - (void)mem; /* suppressing unused parameters warning */ - utype_t u; - decode_utype(&u, inst); - - if (u.rd != 0) - cpu->x[u.rd] = cpu->pc + (u.imm << 12); - - cpu->pc += 4; -} - -static void -exec_itype_control(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) -{ - (void)mem; /* suppressing unused parameters warning */ - itype_t i; - decode_itype(&i, inst); - - if (i.funct3 != 0x0) { - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - if (i.imm == 0x0) { - raise_trap(cpu, TRAP_ECALL, inst); - return; - } else if (i.imm == 0x1) { - raise_trap(cpu, TRAP_EBREAK, inst); - return; - } else { - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - -} - -static void -exec_itype_fence(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) -{ - (void)mem; /* suppressing unused parameters warning */ - itype_t i; - decode_itype(&i, inst); - - if (i.funct3 != 0x0) { - raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - return; - } - - cpu->pc += 4; -} +#include "exec.h" static void load_program(uint8_t *mem) { - /* PROGRAM */ + /* program */ uint32_t arr[] = { /* 0x00500193, // addi x3, x0, 5 @@ -490,13 +21,76 @@ load_program(uint8_t *mem) 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 + 0xfff06193, // ori gp,zero,-1 + 0xfff17213, // andi tp,sp,-1 + 0xfff02293, // slti t0,zero,-1 + 0xfff03313, // sltiu t1,zero,-1 + 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 +0x00a58023, //sb a0,0(a1) +0x0005c503, //lbu a0,0(a1) +0x00150513, //addi a0,a0,1 +0x00a59123, //sh a0,2(a1) +0x00259503, //lh a0,2(a1) +0xeff50513, //addi a0,a0,-257 +0x00a58223, //sb a0,4(a1) +0x00458503, //lb a0,4(a1) +0x00a59323, //sh a0,6(a1) +0x00659503, //lh a0,6(a1) +0x0065d503, //lhu a0,6(a1) +0x00150513, //addi a0,a0,1 +0x00a5a423, //sw a0,8(a1) +0x00000513, //li a0,0 +0x0085a503, //lw a0,8(a1) +0x00000593, //li a1,0 +0x00100073 //ebreak }; + size_t len = sizeof(arr) / sizeof(arr[0]); int b = 0; /*byte*/ @@ -511,45 +105,39 @@ load_program(uint8_t *mem) } } -static inline uint32_t -make_instruction(uint8_t *mem, uint32_t index) -{ - uint32_t inst = 0; - inst |= (uint32_t)(mem[index+0]) << 0; - inst |= (uint32_t)(mem[index+1]) << 8; - inst |= (uint32_t)(mem[index+2]) << 16; - inst |= (uint32_t)(mem[index+3]) << 24; - return inst; - -} - -/* Function pointer dispatch */ -typedef void (*exec_fn)(cpu_state_t *, uint32_t inst, uint8_t *mem); - -static exec_fn opcode_table[128]; -static void -init_opcode_table(void) +void +trap_handler(cpu_state_t *cpu) { - opcode_table[0x33] = exec_rtype; - opcode_table[0x13] = exec_itype; - opcode_table[0x03] = exec_itype_loads; - opcode_table[0x67] = exec_itype_jalr; - opcode_table[0x73] = exec_itype_control; - opcode_table[0x0f] = exec_itype_fence; - opcode_table[0x23] = exec_stype; - opcode_table[0x63] = exec_btype; - opcode_table[0x6f] = exec_jtype; - opcode_table[0x37] = exec_utype; - opcode_table[0x17] = exec_utype_auipc; + if (cpu->status == CPU_TRAPPED) { + printf("CPU trapped\n"); + 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() { - init_opcode_table(); - cpu_state_t cpu = {0}; uint8_t mem[MEMSIZE] = {0}; @@ -570,45 +158,9 @@ main() strncmp(line, "n", 1) == 0 || strncmp(line, "next", 4) == 0) { - uint32_t inst = make_instruction(mem, cpu.pc); - disassemble_instruction(inst); - - /* decode opcode */ - uint16_t opcode = inst & 127; - - exec_fn fn = opcode_table[opcode]; - - if (!fn) { - raise_trap(&cpu, TRAP_ILLEGAL_INSTRUCTION, inst); - } - - fn(&cpu,inst,mem); - - if (cpu.status == CPU_TRAPPED) { - printf("CPU trapped\n"); - 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; - } - return EXIT_FAILURE; - } + cpu_step(&cpu, mem); + trap_handler(&cpu); } else if (strncmp(line, "r", 1) == 0 || strncmp(line, "registers", 9) == 0) { @@ -622,6 +174,10 @@ main() for (int i=0; i<32; i++) { printf("0x%x: 0x%x\n", i, mem[i]); } + printf("\n"); + for (int i=256; i<268; i++) { + printf("0x%x: 0x%x\n", i, mem[i]); + } } else if (strncmp(line, "ip", 2) == 0 || strncmp(line, "pc", 2) == 0) { printf("Instruction poiter:\n"); @@ -631,32 +187,3 @@ main() return 0; } - -/* -int main() { - init_opcode_table(); - - cpu_state_t cpu = {0}; - - uint8_t mem[MEMSIZE] = {0}; - - load_program(mem); - - while (1) { - uint32_t inst = make_instruction(mem, cpu.pc); - - uint16_t opcode = inst & 127; - exec_fn fn = opcode_table[opcode]; - - if (!fn) { - //illegal(NULL, inst); - fprintf(stderr, "Illegal opcode\n"); - return EXIT_FAILURE; - } - - fn(&cpu,inst,mem); - } - - return 0; -} -*/