From 40b4d52e905227849785f6520d0381f19657820a Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sun, 12 May 2024 11:24:16 +0200 Subject: Reorganize all this shit --- emulator/hart.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 emulator/hart.c (limited to 'emulator/hart.c') 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 +#include +#include +#include +#include + +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); + } +} + -- cgit