diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-05-12 11:24:16 +0200 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-05-12 11:24:16 +0200 |
commit | 40b4d52e905227849785f6520d0381f19657820a (patch) | |
tree | 91eb8549419c14f4c8880693109eb41c8c650552 /emulator/hart.c | |
parent | b9dd0160640adcbb1b5a66247b274bf99a7de705 (diff) |
Reorganize all this shit
Diffstat (limited to 'emulator/hart.c')
-rw-r--r-- | emulator/hart.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/emulator/hart.c b/emulator/hart.c new file mode 100644 index 0000000..cfca837 --- /dev/null +++ b/emulator/hart.c @@ -0,0 +1,446 @@ +#include "emulator/hart.h"
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+static inline uint32_t sign_extend(uint32_t word, uint32_t size)
+{
+ const uint32_t mask = 1U << (size - 1);
+ return (word ^ mask) - mask;
+}
+
+struct Instruction
+{
+ uint8_t opcode;
+ uint8_t rs1;
+ uint8_t rs2;
+ uint8_t rd;
+ uint8_t funct3;
+ uint8_t funct7;
+ uint32_t imm;
+};
+typedef struct Instruction Instruction;
+
+static Instruction decode_r_type(uint32_t word)
+{
+ Instruction instruction = {0};
+ instruction.opcode = word & 0x7F;
+ instruction.rd = (word >> 7) & 0x1F;
+ instruction.funct3 = (word >> 12) & 0x07;
+ instruction.rs1 = (word >> 15) & 0x1F;
+ instruction.rs2 = (word >> 20) & 0x1F;
+ instruction.funct7 = word >> 25;
+ return instruction;
+};
+
+static Instruction decode_i_type(uint32_t word)
+{
+ Instruction instruction = {0};
+ instruction.opcode = word & 0x7F;
+ instruction.rd = (word >> 7) & 0x1F;
+ instruction.funct3 = (word >> 12) & 0x07;
+ instruction.rs1 = (word >> 15) & 0x1F;
+ instruction.imm = sign_extend(word >> 20, 12);
+ return instruction;
+};
+
+static Instruction decode_s_type(uint32_t word)
+{
+ Instruction instruction = {0};
+ instruction.opcode = word & 0x7F;
+ instruction.funct3 = (word >> 12) & 0x07;
+ instruction.rs1 = (word >> 15) & 0x1F;
+ instruction.rs2 = (word >> 20) & 0x1F;
+ instruction.imm = sign_extend(((word >> 7) & 0x1F) | (word >> 25), 12);
+ return instruction;
+};
+
+static Instruction decode_b_type(uint32_t word)
+{
+ Instruction instruction = decode_s_type(word);
+ instruction.imm = ((instruction.imm << 11) & 0x800) | (instruction.imm & 0xfffff7ff);
+ return instruction;
+};
+
+static Instruction decode_u_type(uint32_t word)
+{
+ Instruction instruction = {0};
+ instruction.opcode = word & 0x7F;
+ instruction.rd = (word >> 7) & 0x1F;
+ instruction.imm = word & 0xFFFFF000;
+ return instruction;
+};
+
+static Instruction decode_j_type(uint32_t word)
+{
+ Instruction instruction = {0};
+ instruction.opcode = word & 0x7F;
+ instruction.rd = (word >> 7) & 0x1F;
+ instruction.imm = sign_extend(
+ ((word & 0x80000000) >> 11) |
+ ((word & 0x000FF000) >> 0) |
+ ((word & 0x00100000) >> 9) |
+ ((word & 0x7FE00000) >> 20), 21);
+ return instruction;
+}
+
+static void execute_op_imm(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_i_type(instruction);
+ if (inst.rd == 0) return;
+
+ switch (inst.funct3)
+ {
+ case 0: // ADDI
+ hart->regs[inst.rd] = hart->regs[inst.rs1] + inst.imm;
+ break;
+ case 1: // SLLI
+ hart->regs[inst.rd] = hart->regs[inst.rs1] << (inst.imm & 0x1F);
+ break;
+ case 2: // SLTI
+ hart->regs[inst.rd] = (int32_t)hart->regs[inst.rs1] < (int32_t)inst.imm ? 1 : 0;
+ break;
+ case 3: // SLTIU
+ hart->regs[inst.rd] = hart->regs[inst.rs1] < inst.imm ? 1 : 0;
+ break;
+ case 4: // XORI
+ hart->regs[inst.rd] = hart->regs[inst.rs1] ^ inst.imm;
+ break;
+ case 5: // SRLI, SRAI
+ {
+ const uint32_t shamt = inst.imm & 0x1F;
+ uint32_t res = hart->regs[inst.rs1] >> shamt;
+ if ((inst.imm & 0x400) && shamt > 0) { res = sign_extend(res, 32 - shamt); }
+ hart->regs[inst.rd] = res;
+ break;
+ }
+ case 6: // ORI
+ hart->regs[inst.rd] = hart->regs[inst.rs1] | inst.imm;
+ break;
+ case 7: // ANDI
+ hart->regs[inst.rd] = hart->regs[inst.rs1] & inst.imm;
+ break;
+ default:
+ assert(!"Unhandled OP-IMM");
+ }
+}
+
+static void execute_op(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_r_type(instruction);
+ if (inst.rd == 0) return;
+
+ switch (inst.funct3)
+ {
+ case 0: // ADD, SUB
+ if (instruction & 0x40000000)
+ {
+ hart->regs[inst.rd] = hart->regs[inst.rs1] - hart->regs[inst.rs2];
+ }
+ else
+ {
+ hart->regs[inst.rd] = hart->regs[inst.rs1] + hart->regs[inst.rs2];
+ }
+ break;
+ case 1: // SLL
+ hart->regs[inst.rd] = hart->regs[inst.rs1] << (hart->regs[inst.rs2] & 0x1F);
+ break;
+ case 2: // SLT
+ hart->regs[inst.rd] = (int32_t)hart->regs[inst.rs1] < (int32_t)hart->regs[inst.rs2] ? 1 : 0;
+ break;
+ case 3: // SLTU
+ hart->regs[inst.rd] = hart->regs[inst.rs1] < hart->regs[inst.rs2] ? 1 : 0;
+ break;
+ case 4: // XOR
+ hart->regs[inst.rd] = hart->regs[inst.rs1] ^ hart->regs[inst.rs2];
+ break;
+ case 5: // SRL, SRA
+ {
+ const uint32_t shamt = hart->regs[inst.rs2] & 0x1F;
+ uint32_t res = hart->regs[inst.rs1] >> shamt;
+ if ((instruction & 0x40000000) && shamt > 0) { res = sign_extend(res, 32 - shamt); }
+ hart->regs[inst.rd] = res;
+ break;
+ }
+ case 6: // OR
+ hart->regs[inst.rd] = hart->regs[inst.rs1] | hart->regs[inst.rs2];
+ break;
+ case 7: // AND
+ hart->regs[inst.rd] = hart->regs[inst.rs1] & hart->regs[inst.rs2];
+ break;
+ default:
+ assert(!"Unhandled OP-IMM");
+ }
+}
+
+static void execute_branch(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_b_type(instruction);
+ const uint32_t r1 = hart->regs[inst.rs1];
+ const uint32_t r2 = hart->regs[inst.rs2];
+ bool take_branch = false;
+
+ switch (inst.funct3)
+ {
+ case 0: take_branch = (r1 == r2); break;
+ case 1: take_branch = (r1 != r2); break;
+ case 4: take_branch = (r1 < r2); break;
+ case 5: take_branch = (r1 >= r2); break;
+ case 6: take_branch = ((int32_t)r1 < (int32_t)r2); break;
+ case 7: take_branch = ((int32_t)r1 >= (int32_t)r2); break;
+ }
+
+ if (take_branch)
+ {
+ hart->pc += inst.imm;
+ }
+ else
+ {
+ hart->pc += 4;
+ }
+}
+
+static inline uint32_t load_size(Hart* hart, uint32_t address, uint32_t size)
+{
+ if ((address & 0x80000000) == 0)
+ {
+ assert(address + size < hart->mem_size);
+ uint32_t value = 0;
+ memcpy(&value, hart->mem + address, size);
+ return value;
+ }
+
+ return 0;
+}
+
+static uint32_t load_byte(Hart* hart, uint32_t address)
+{
+ return load_size(hart, address, 1);
+}
+
+static uint32_t load_half(Hart* hart, uint32_t address)
+{
+ return load_size(hart, address, 2);
+}
+
+static uint32_t load_word(Hart* hart, uint32_t address)
+{
+ return load_size(hart, address, 4);
+}
+
+static inline void store_size(Hart* hart, uint32_t address, uint32_t value, uint32_t size)
+{
+ if ((address & 0x80000000) == 0)
+ {
+ assert(address + size < hart->mem_size);
+ memcpy(hart->mem + address, &value, size);
+ }
+ else if (address == 0x80000000)
+ {
+ fwrite(&value, 1, size, stdout);
+ }
+}
+
+static void store_byte(Hart* hart, uint32_t address, uint8_t value)
+{
+ store_size(hart, address, value, 1);
+}
+
+static void store_half(Hart* hart, uint32_t address, uint16_t value)
+{
+ store_size(hart, address, value, 2);
+}
+
+static void store_word(Hart* hart, uint32_t address, uint32_t value)
+{
+ store_size(hart, address, value, 4);
+}
+
+static void execute_op_load(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_i_type(instruction);
+ const uint32_t address = hart->regs[inst.rs1] + inst.imm;
+
+ uint32_t value = 0;
+
+ switch (inst.funct3 & 0x03)
+ {
+ case 0:
+ value = load_byte(hart, address);
+ if ((inst.funct3 & 0x40) == 0)
+ {
+ value = sign_extend(value, 8);
+ }
+ break;
+ case 1:
+ value = load_half(hart, address);
+ if ((inst.funct3 & 0x40) == 0)
+ {
+ value = sign_extend(value, 16);
+ }
+ break;
+ case 2:
+ value = load_byte(hart, address);
+ break;
+ default:
+ assert(!"Unhandled load size");
+ break;
+ }
+
+ if (inst.rd != 0)
+ {
+ hart->regs[inst.rd] = value;
+ }
+}
+
+static void execute_op_store(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_s_type(instruction);
+ const uint32_t address = hart->regs[inst.rs1] + inst.imm;
+ const uint32_t value = hart->regs[inst.rs2];
+
+ switch (inst.funct3 & 0x03)
+ {
+ case 0: store_byte(hart, address, value & 0xFF); break;
+ case 1: store_half(hart, address, value & 0xFFFF); break;
+ case 2: store_word(hart, address, value); break;
+ default:
+ assert(!"Unhandled store size");
+ break;
+ }
+}
+
+static void execute_misc_mem(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_i_type(instruction);
+
+ if (inst.funct3 == 0)
+ {
+ // FENCE
+ }
+ else
+ {
+ assert(!"Unhandled MISC-MEM instruction");
+ }
+}
+
+static void execute_system(Hart* hart, uint32_t instruction)
+{
+ const Instruction inst = decode_i_type(instruction);
+
+ if (inst.funct3 == 0 && inst.rs1 == 0 && inst.rd == 0)
+ {
+ if (inst.imm == 0)
+ {
+ // ECALL
+ }
+ else if (inst.imm == 1)
+ {
+ // EBREAK
+ }
+ else
+ {
+ assert(!"Unhandled SYSTEM/PRIV instruction");
+ }
+ }
+ else
+ {
+ assert(!"Unhandled SYSTEM instruction");
+ }
+}
+
+void execute(Hart* hart, uint32_t instruction)
+{
+ switch (instruction & 0x7f)
+ {
+ case 0x03:
+ execute_op_load(hart, instruction);
+ hart->pc += 4;
+ break;
+ case 0x0F:
+ execute_misc_mem(hart, instruction);
+ hart->pc += 4;
+ break;
+ case 0x13:
+ execute_op_imm(hart, instruction);
+ hart->pc += 4;
+ break;
+ case 0x17: // AUIPC
+ {
+ Instruction inst = decode_u_type(instruction);
+ if (inst.rd != 0)
+ {
+ hart->regs[inst.rd] = inst.imm + hart->pc;
+ }
+ hart->pc += 4;
+ break;
+ }
+ case 0x23:
+ execute_op_store(hart, instruction);
+ hart->pc += 4;
+ break;
+ case 0x33:
+ execute_op(hart, instruction);
+ hart->pc += 4;
+ break;
+ case 0x37: // LUI
+ {
+ Instruction inst = decode_u_type(instruction);
+ if (inst.rd != 0)
+ {
+ hart->regs[inst.rd] = inst.imm;
+ }
+ hart->pc += 4;
+ break;
+ }
+ case 0x63:
+ execute_branch(hart, instruction);
+ break;
+ case 0x67: // JALR
+ {
+ Instruction inst = decode_i_type(instruction);
+ assert(inst.funct3 == 0);
+ if (inst.rd != 0)
+ {
+ hart->regs[inst.rd] = hart->pc + 4;
+ }
+ hart->pc = (hart->regs[inst.rs1] + inst.imm) & 0xFFFFFFFE;
+ break;
+ }
+ case 0x6F: // JAL
+ {
+ Instruction inst = decode_j_type(instruction);
+ if (inst.rd != 0)
+ {
+ hart->regs[inst.rd] = hart->pc + 4;
+ }
+ hart->pc += inst.imm;
+ break;
+ }
+ case 0x73:
+ {
+ execute_system(hart, instruction);
+ hart->pc += 4;
+ break;
+ }
+ default:
+ assert(!"Unhandled opcode");
+ }
+
+ assert(hart->regs[0] == 0);
+}
+
+void execute_from(Hart* hart, uint32_t start_address)
+{
+ hart->pc = start_address;
+
+ while (true)
+ {
+ uint32_t instruction = load_word(hart, hart->pc);
+ execute(hart, instruction);
+ }
+}
+
|