From 8ebaea458322a2df925cf351bf6b4f300567150d Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sun, 12 May 2024 15:29:16 +0200 Subject: Multiplication --- emulator/hart.c | 117 ++++++++++++++++++++++++++++++++++----------------- emulator/hart_test.c | 44 ++++++++++++++++--- 2 files changed, 117 insertions(+), 44 deletions(-) diff --git a/emulator/hart.c b/emulator/hart.c index db43cd1..101d77f 100644 --- a/emulator/hart.c +++ b/emulator/hart.c @@ -45,6 +45,12 @@ static inline uint32_t sign_extend(uint32_t word, uint32_t size) return (word ^ mask) - mask; } +static inline uint64_t sign_extend_64(uint64_t word, uint32_t size) +{ + const uint64_t mask = 1ULL << (size - 1); + return (word ^ mask) - mask; +} + struct Instruction { uint8_t opcode; @@ -165,47 +171,82 @@ static void execute_op(Hart* hart, uint32_t instruction) { const Instruction inst = decode_r_type(instruction); if (inst.rd == 0) return; - - switch (inst.funct3) + + uint32_t bank = inst.funct7 & 0x5F; + if (bank == 0) { - case 0: // ADD, SUB - if (instruction & 0x40000000) + switch (inst.funct3) + { + case 0: // ADD, SUB + if (inst.funct7 & 0x20) + { + 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 { - hart->regs[inst.rd] = hart->regs[inst.rs1] - hart->regs[inst.rs2]; + const uint32_t shamt = hart->regs[inst.rs2] & 0x1F; + uint32_t res = hart->regs[inst.rs1] >> shamt; + if ((inst.funct7 & 0x20) && shamt > 0) { res = sign_extend(res, 32 - shamt); } + hart->regs[inst.rd] = res; + break; } - else + 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, bank = 0"); + break; + } + } + else if (bank == 1) + { + if ((inst.funct3 & 0x8) == 0) + { + uint64_t a = hart->regs[inst.rs1]; + uint64_t b = hart->regs[inst.rs2]; + + switch (inst.funct3 & 0x3) { - hart->regs[inst.rd] = hart->regs[inst.rs1] + hart->regs[inst.rs2]; + case 1: // MULH + b = sign_extend_64(b, 32); + case 2: // MULHSU + a = sign_extend_64(a, 32); + case 0: // MUL + case 3: // MULHU + break; } - 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 + + uint64_t r = a * b; + hart->regs[inst.rd] = (inst.funct3 == 0) ? r & 0xFFFFFFFF : r >> 32; + } + else { - 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; + assert(!"Unhandled M OP"); } - 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"); + } + else + { + assert(!"Unhandled OP bank"); } } @@ -292,7 +333,7 @@ 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) +static void execute_load(Hart* hart, uint32_t instruction) { const Instruction inst = decode_i_type(instruction); const uint32_t address = hart->regs[inst.rs1] + inst.imm; @@ -329,7 +370,7 @@ static void execute_op_load(Hart* hart, uint32_t instruction) } } -static void execute_op_store(Hart* hart, uint32_t instruction) +static void execute_store(Hart* hart, uint32_t instruction) { const Instruction inst = decode_s_type(instruction); const uint32_t address = hart->regs[inst.rs1] + inst.imm; @@ -545,7 +586,7 @@ void execute(Hart* hart, uint32_t instruction) switch (instruction & 0x7f) { case 0x03: - execute_op_load(hart, instruction); + execute_load(hart, instruction); hart->pc += 4; break; case 0x0F: @@ -567,7 +608,7 @@ void execute(Hart* hart, uint32_t instruction) break; } case 0x23: - execute_op_store(hart, instruction); + execute_store(hart, instruction); hart->pc += 4; break; case 0x33: diff --git a/emulator/hart_test.c b/emulator/hart_test.c index 5f2549b..1611804 100644 --- a/emulator/hart_test.c +++ b/emulator/hart_test.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "emulator/hart.h" @@ -229,7 +230,42 @@ static void test_branch() assert(hart.pc == 124); } -void test() +static void test_m() +{ + struct Hart hart = {0}; + + hart.regs[1] = 0xFFFFFFFE; + hart.regs[2] = 4; + + execute(&hart, 0x022081b3); // mul x3, x1, x2 + assert(hart.regs[3] == 0xFFFFFFF8); + + execute(&hart, 0x0220b1b3); // mulhu x3, x1, x2 + assert(hart.regs[3] == 0x00000003); + + execute(&hart, 0x0220a1b3); // mulhsu x3, x1, x2 + assert(hart.regs[3] == 0xFFFFFFFF); + + execute(&hart, 0x022091b3); // mulh x3, x1, x2 + assert(hart.regs[3] == 0xFFFFFFFF); + + hart.regs[1] = 0xFFFFFFFE; + hart.regs[2] = 0xFFFFFFFE; + + execute(&hart, 0x022081b3); // mul x3, x1, x2 + assert(hart.regs[3] == 0x00000004); + + execute(&hart, 0x0220b1b3); // mulhu x3, x1, x2 + assert(hart.regs[3] == 0xFFFFFFFC); + + execute(&hart, 0x0220a1b3); // mulhsu x3, x1, x2 + assert(hart.regs[3] == 0xFFFFFFFE); + + execute(&hart, 0x022091b3); // mulh x3, x1, x2 + assert(hart.regs[3] == 0x00000000); +} + +int main(int argc, char* argv[]) { test_addi(); test_slti_sltiu(); @@ -240,10 +276,6 @@ void test() test_jal(); test_jalr(); test_branch(); -} - -int main(int argc, char* argv[]) -{ - test(); + test_m(); return EXIT_SUCCESS; } -- cgit