summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Le Rouzic <steven.lerouzic@gmail.com>2024-05-12 15:29:16 +0200
committerSteven Le Rouzic <steven.lerouzic@gmail.com>2024-05-12 15:29:16 +0200
commit8ebaea458322a2df925cf351bf6b4f300567150d (patch)
tree83f292e2ab7b3f8cab49d0e0bad2e27ac96f4eee
parent822fd8616cf3d960cb2df1d2f25452ef57ea854b (diff)
Multiplication
-rw-r--r--emulator/hart.c117
-rw-r--r--emulator/hart_test.c44
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 <inttypes.h>
#include <assert.h>
#include <stdbool.h>
+#include <stdio.h>
#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;
}