Multiplication
This commit is contained in:
@ -45,6 +45,12 @@ static inline uint32_t sign_extend(uint32_t word, uint32_t size)
|
|||||||
return (word ^ mask) - mask;
|
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
|
struct Instruction
|
||||||
{
|
{
|
||||||
uint8_t opcode;
|
uint8_t opcode;
|
||||||
@ -166,10 +172,13 @@ static void execute_op(Hart* hart, uint32_t instruction)
|
|||||||
const Instruction inst = decode_r_type(instruction);
|
const Instruction inst = decode_r_type(instruction);
|
||||||
if (inst.rd == 0) return;
|
if (inst.rd == 0) return;
|
||||||
|
|
||||||
|
uint32_t bank = inst.funct7 & 0x5F;
|
||||||
|
if (bank == 0)
|
||||||
|
{
|
||||||
switch (inst.funct3)
|
switch (inst.funct3)
|
||||||
{
|
{
|
||||||
case 0: // ADD, SUB
|
case 0: // ADD, SUB
|
||||||
if (instruction & 0x40000000)
|
if (inst.funct7 & 0x20)
|
||||||
{
|
{
|
||||||
hart->regs[inst.rd] = hart->regs[inst.rs1] - hart->regs[inst.rs2];
|
hart->regs[inst.rd] = hart->regs[inst.rs1] - hart->regs[inst.rs2];
|
||||||
}
|
}
|
||||||
@ -194,7 +203,7 @@ static void execute_op(Hart* hart, uint32_t instruction)
|
|||||||
{
|
{
|
||||||
const uint32_t shamt = hart->regs[inst.rs2] & 0x1F;
|
const uint32_t shamt = hart->regs[inst.rs2] & 0x1F;
|
||||||
uint32_t res = hart->regs[inst.rs1] >> shamt;
|
uint32_t res = hart->regs[inst.rs1] >> shamt;
|
||||||
if ((instruction & 0x40000000) && shamt > 0) { res = sign_extend(res, 32 - shamt); }
|
if ((inst.funct7 & 0x20) && shamt > 0) { res = sign_extend(res, 32 - shamt); }
|
||||||
hart->regs[inst.rd] = res;
|
hart->regs[inst.rd] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -205,7 +214,39 @@ static void execute_op(Hart* hart, uint32_t instruction)
|
|||||||
hart->regs[inst.rd] = hart->regs[inst.rs1] & hart->regs[inst.rs2];
|
hart->regs[inst.rd] = hart->regs[inst.rs1] & hart->regs[inst.rs2];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(!"Unhandled OP-IMM");
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t r = a * b;
|
||||||
|
hart->regs[inst.rd] = (inst.funct3 == 0) ? r & 0xFFFFFFFF : r >> 32;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(!"Unhandled M OP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
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 Instruction inst = decode_i_type(instruction);
|
||||||
const uint32_t address = hart->regs[inst.rs1] + inst.imm;
|
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 Instruction inst = decode_s_type(instruction);
|
||||||
const uint32_t address = hart->regs[inst.rs1] + inst.imm;
|
const uint32_t address = hart->regs[inst.rs1] + inst.imm;
|
||||||
@ -545,7 +586,7 @@ void execute(Hart* hart, uint32_t instruction)
|
|||||||
switch (instruction & 0x7f)
|
switch (instruction & 0x7f)
|
||||||
{
|
{
|
||||||
case 0x03:
|
case 0x03:
|
||||||
execute_op_load(hart, instruction);
|
execute_load(hart, instruction);
|
||||||
hart->pc += 4;
|
hart->pc += 4;
|
||||||
break;
|
break;
|
||||||
case 0x0F:
|
case 0x0F:
|
||||||
@ -567,7 +608,7 @@ void execute(Hart* hart, uint32_t instruction)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x23:
|
case 0x23:
|
||||||
execute_op_store(hart, instruction);
|
execute_store(hart, instruction);
|
||||||
hart->pc += 4;
|
hart->pc += 4;
|
||||||
break;
|
break;
|
||||||
case 0x33:
|
case 0x33:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "emulator/hart.h"
|
#include "emulator/hart.h"
|
||||||
|
|
||||||
@ -229,7 +230,42 @@ static void test_branch()
|
|||||||
assert(hart.pc == 124);
|
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_addi();
|
||||||
test_slti_sltiu();
|
test_slti_sltiu();
|
||||||
@ -240,10 +276,6 @@ void test()
|
|||||||
test_jal();
|
test_jal();
|
||||||
test_jalr();
|
test_jalr();
|
||||||
test_branch();
|
test_branch();
|
||||||
}
|
test_m();
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
test();
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user