M extension for RV64

This commit is contained in:
Màrius Montón 2022-09-14 20:02:07 +02:00
parent 56b00aecd8
commit 643b880be3
No known key found for this signature in database
GPG Key ID: FA199E7A752699F0
3 changed files with 393 additions and 123 deletions

View File

@ -25,7 +25,11 @@ namespace riscv_tlm {
OP_M_DIVU, OP_M_DIVU,
OP_M_REM, OP_M_REM,
OP_M_REMU, OP_M_REMU,
OP_M_MULW,
OP_M_DIVW,
OP_M_DIVUW,
OP_M_REMW,
OP_M_REMUW,
OP_M_ERROR OP_M_ERROR
} op_M_Codes; } op_M_Codes;
@ -38,6 +42,11 @@ namespace riscv_tlm {
M_DIVU = 0b101, M_DIVU = 0b101,
M_REM = 0b110, M_REM = 0b110,
M_REMU = 0b111, M_REMU = 0b111,
M_MULW = 0b000,
M_DIVW = 0b100,
M_DIVUW = 0b101,
M_REMW = 0b110,
M_REMUW = 0b111,
} M_Codes; } M_Codes;
/** /**
@ -61,6 +70,7 @@ namespace riscv_tlm {
*/ */
[[nodiscard]] op_M_Codes decode() const { [[nodiscard]] op_M_Codes decode() const {
if (this->m_instr.range(6,0) == 0b110011) {
switch (opcode()) { switch (opcode()) {
case M_MUL: case M_MUL:
return OP_M_MUL; return OP_M_MUL;
@ -90,6 +100,29 @@ namespace riscv_tlm {
return OP_M_ERROR; return OP_M_ERROR;
break; break;
} }
} else {
switch (opcode()) {
case M_MULW:
return OP_M_MULW;
break;
case M_DIVW:
return OP_M_DIVW;
break;
case M_DIVUW:
return OP_M_DIVUW;
break;
case M_REMW:
return OP_M_REMW;
break;
case M_REMUW:
return OP_M_REMUW;
break;
[[unlikely]] default:
return OP_M_ERROR;
break;
}
}
return OP_M_ERROR; return OP_M_ERROR;
} }
@ -100,107 +133,42 @@ namespace riscv_tlm {
void Exec_M_MUL() const { void Exec_M_MUL() const {
unsigned int rd, rs1, rs2; unsigned int rd, rs1, rs2;
std::int32_t multiplier, multiplicand; signed_T multiplier, multiplicand;
std::int64_t result; std::int64_t result;
rd = this->get_rd(); rd = this->get_rd();
rs1 = this->get_rs1(); rs1 = this->get_rs1();
rs2 = this->get_rs2(); rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(extension_base<T>::regs->getValue(rs1)); multiplier = static_cast<signed_T>(this->regs->getValue(rs1));
multiplicand = static_cast<std::int32_t>(extension_base<T>::regs->getValue(rs2)); multiplicand = static_cast<signed_T>(this->regs->getValue(rs2));
result = static_cast<std::int64_t>(multiplier * multiplicand); result = static_cast<std::int64_t>(multiplier * multiplicand);
result = result & 0x00000000FFFFFFFF;
this->regs->setValue(rd, static_cast<std::int32_t>(result));
this->logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d} * x{:d} -> x{:d}({:d})", this->regs->setValue(rd, static_cast<signed_T>(result));
sc_core::sc_time_stamp().value(),
extension_base<T>::regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_MULH() const { this->logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
unsigned int rd, rs1, rs2;
std::int32_t multiplier, multiplicand;
std::int64_t result;
std::int32_t ret_value;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
this->regs->setValue(rd, ret_value);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(), sc_core::sc_time_stamp().value(),
this->regs->getPC(), this->regs->getPC(),
rs1, rs2, rd, result); rs1, rs2, multiplier, multiplicand, rd, result);
} }
void Exec_M_MULHSU() const { void Exec_M_MULH() const;
unsigned int rd, rs1, rs2;
std::int32_t multiplier;
std::uint32_t multiplicand;
std::int64_t result;
rd = this->get_rd(); void Exec_M_MULHSU() const;
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1)); void Exec_M_MULHU() const;
multiplicand = this->regs->getValue(rs2);
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
result = (result >> 32) & 0x00000000FFFFFFFF;
this->regs->setValue(rd, static_cast<std::int32_t>(result));
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_MULHU() const {
unsigned int rd, rs1, rs2;
std::uint32_t multiplier, multiplicand;
std::uint64_t result;
std::int32_t ret_value;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
this->regs->setValue(rd, ret_value);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_DIV() const { void Exec_M_DIV() const {
unsigned int rd, rs1, rs2; unsigned int rd, rs1, rs2;
std::int32_t divisor, dividend; signed_T divisor, dividend;
std::int64_t result; std::int64_t result;
rd = this->get_rd(); rd = this->get_rd();
rs1 = this->get_rs1(); rs1 = this->get_rs1();
rs2 = this->get_rs2(); rs2 = this->get_rs2();
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1)); dividend = this->regs->getValue(rs1);
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2)); divisor = this->regs->getValue(rs2);
if (divisor == 0) { if (divisor == 0) {
result = -1; result = -1;
@ -208,10 +176,9 @@ namespace riscv_tlm {
result = 0x0000000080000000; result = 0x0000000080000000;
} else { } else {
result = dividend / divisor; result = dividend / divisor;
result = result & 0x00000000FFFFFFFF;
} }
this->regs->setValue(rd, static_cast<std::int32_t>(result)); this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})", this->logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(), sc_core::sc_time_stamp().value(),
@ -221,7 +188,7 @@ namespace riscv_tlm {
void Exec_M_DIVU() const { void Exec_M_DIVU() const {
unsigned int rd, rs1, rs2; unsigned int rd, rs1, rs2;
std::uint32_t divisor, dividend; unsigned_T divisor, dividend;
std::uint64_t result; std::uint64_t result;
rd = this->get_rd(); rd = this->get_rd();
@ -235,10 +202,9 @@ namespace riscv_tlm {
result = -1; result = -1;
} else { } else {
result = dividend / divisor; result = dividend / divisor;
result = result & 0x00000000FFFFFFFF;
} }
this->regs->setValue(rd, static_cast<std::int32_t>(result)); this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})", this->logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(), sc_core::sc_time_stamp().value(),
@ -248,15 +214,15 @@ namespace riscv_tlm {
void Exec_M_REM() const { void Exec_M_REM() const {
unsigned int rd, rs1, rs2; unsigned int rd, rs1, rs2;
std::int32_t divisor, dividend; signed_T divisor, dividend;
std::int32_t result; signed_T result;
rd = this->get_rd(); rd = this->get_rd();
rs1 = this->get_rs1(); rs1 = this->get_rs1();
rs2 = this->get_rs2(); rs2 = this->get_rs2();
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1)); dividend = this->regs->getValue(rs1);
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2)); divisor = this->regs->getValue(rs2);
if (divisor == 0) { if (divisor == 0) {
result = dividend; result = dividend;
@ -268,7 +234,7 @@ namespace riscv_tlm {
this->regs->setValue(rd, result); this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} / x{:d} -> x{:d}({:d})", this->logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} % x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(), sc_core::sc_time_stamp().value(),
this->regs->getPC(), this->regs->getPC(),
rs1, rs2, rd, result); rs1, rs2, rd, result);
@ -276,15 +242,15 @@ namespace riscv_tlm {
void Exec_M_REMU() const { void Exec_M_REMU() const {
unsigned int rd, rs1, rs2; unsigned int rd, rs1, rs2;
std::uint32_t divisor, dividend; unsigned_T divisor, dividend;
std::uint32_t result; unsigned_T result;
rd = this->get_rd(); rd = this->get_rd();
rs1 = this->get_rs1(); rs1 = this->get_rs1();
rs2 = this->get_rs2(); rs2 = this->get_rs2();
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1)); dividend = this->regs->getValue(rs1);
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2)); divisor = this->regs->getValue(rs2);
if (divisor == 0) { if (divisor == 0) {
result = dividend; result = dividend;
@ -292,14 +258,145 @@ namespace riscv_tlm {
result = dividend % divisor; result = dividend % divisor;
} }
this->regs->setValue(rd, static_cast<std::int32_t>(result)); this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} / x{:d} -> x{:d}({:d})", this->logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} % x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(), sc_core::sc_time_stamp().value(),
this->regs->getPC(), this->regs->getPC(),
rs1, rs2, rd, result); rs1, rs2, rd, result);
} }
void Exec_M_MULW() const {
unsigned int rd, rs1, rs2;
std::uint32_t multiplier, multiplicand;
std::int64_t result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
multiplicand = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
result = static_cast<std::int32_t>((multiplier * multiplicand) & 0x00000000FFFFFFFF);
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULW: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, multiplier, multiplicand, rd, result);
}
void Exec_M_DIVW() const {
unsigned int rd, rs1, rs2;
std::int32_t divisor, dividend;
std::int64_t result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
if (divisor == 0) {
result = -1;
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
result = 0x0000000080000000;
} else {
result = dividend / divisor;
}
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVW: x{:d} / x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_DIVUW() const {
unsigned int rd, rs1, rs2;
std::uint32_t divisor, dividend;
std::int64_t result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
dividend = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
divisor = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
if (divisor == 0) {
result = -1;
} else if ((divisor == 0xFFFFFFFF) && (dividend == static_cast<std::uint32_t>(0x80000000))) {
result = 0x0000000080000000;
} else {
result = static_cast<std::int32_t>(dividend / divisor);
}
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVUW: x{:d} / x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_REMW() const {
unsigned int rd, rs1, rs2;
std::int32_t divisor, dividend;
signed_T result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
if (divisor == 0) {
result = dividend;
} else if (divisor == -1) {
result = 0;
} else {
result = dividend % divisor;
}
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.REMW: x{:d} % x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
void Exec_M_REMUW() const {
unsigned int rd, rs1, rs2;
std::uint32_t divisor, dividend;
std::int64_t result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
dividend = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
divisor = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
if (divisor == 0) {
result = static_cast<std::int32_t>(dividend);
} else {
result = static_cast<std::int32_t>(dividend % divisor);
}
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.REMUW: x{:d}({:d}) % x{:d}({:d}) -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, dividend, rs2, divisor, rd, result);
}
bool process_instruction(Instruction &inst) { bool process_instruction(Instruction &inst) {
this->setInstr(inst.getInstr()); this->setInstr(inst.getInstr());
@ -328,6 +425,21 @@ namespace riscv_tlm {
case OP_M_REMU: case OP_M_REMU:
Exec_M_REMU(); Exec_M_REMU();
break; break;
case OP_M_MULW:
Exec_M_MULW();
break;
case OP_M_DIVW:
Exec_M_DIVW();
break;
case OP_M_DIVUW:
Exec_M_DIVUW();
break;
case OP_M_REMW:
Exec_M_REMW();
break;
case OP_M_REMUW:
Exec_M_REMUW();
break;
[[unlikely]] default: [[unlikely]] default:
std::cout << "M instruction not implemented yet" << "\n"; std::cout << "M instruction not implemented yet" << "\n";
inst.dump(); inst.dump();

View File

@ -15,7 +15,7 @@ namespace riscv_tlm {
} }
extension_t Instruction::check_extension() const { extension_t Instruction::check_extension() const {
if (((m_instr & 0x0000007F) == 0b0110011) if ((((m_instr & 0x0000007F) == 0b0110011) || ((m_instr & 0x0000007F) == 0b0111011))
&& (((m_instr & 0x7F000000) >> 25) == 0b0000001)) { && (((m_instr & 0x7F000000) >> 25) == 0b0000001)) {
return M_EXTENSION; return M_EXTENSION;
} else if ((m_instr & 0x0000007F) == 0b0101111) { } else if ((m_instr & 0x0000007F) == 0b0101111) {

View File

@ -7,4 +7,162 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "M_extension.h" #include "M_extension.h"
namespace riscv_tlm {
// RV32
template<>
void M_extension<std::uint32_t>::Exec_M_MULH() const {
unsigned int rd, rs1, rs2;
signed_T multiplier, multiplicand;
std::int64_t result;
signed_T ret_value;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = this->regs->getValue(rs1);
multiplicand = this->regs->getValue(rs2);
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
this->regs->setValue(rd, ret_value);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, multiplier, rs2, multiplicand, rd, result);
}
template<>
void M_extension<std::uint32_t>::Exec_M_MULHSU() const {
unsigned int rd, rs1, rs2;
std::int32_t multiplier;
std::uint32_t multiplicand;
std::int64_t result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
multiplicand = this->regs->getValue(rs2);
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
result = (result >> 32) & 0x00000000FFFFFFFF;
this->regs->setValue(rd, static_cast<std::int32_t>(result));
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
template<>
void M_extension<std::uint32_t>::Exec_M_MULHU() const {
unsigned int rd, rs1, rs2;
std::uint32_t multiplier, multiplicand;
std::uint64_t result;
std::int32_t ret_value;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
this->regs->setValue(rd, ret_value);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
// RV64
// I need to use SystemC bigint with 128 bits to perform 64 x 64 bits multiplication and keep the high half
template<>
void M_extension<std::uint64_t>::Exec_M_MULH() const {
unsigned int rd, rs1, rs2;
signed_T multiplier, multiplicand;
signed_T result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = this->regs->getValue(rs1);
multiplicand = this->regs->getValue(rs2);
sc_dt::sc_bigint<128> mul = multiplier;
sc_dt::sc_bigint<128> muld = multiplicand;
sc_dt::sc_bigint<128> res = mul * muld;
result = res.range(127, 64).to_int64();
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, multiplier, rs2, multiplicand, rd, result);
}
template<>
void M_extension<std::uint64_t>::Exec_M_MULHSU() const {
unsigned int rd, rs1, rs2;
signed_T multiplier;
unsigned_T multiplicand;
signed_T result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = this->regs->getValue(rs1);
multiplicand = this->regs->getValue(rs2);
sc_dt::sc_bigint<128> mul = multiplier;
sc_dt::sc_bigint<128> muld = multiplicand;
sc_dt::sc_bigint<128> res = mul * muld;
result = res.range(127, 64).to_int64();
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
template<>
void M_extension<std::uint64_t>::Exec_M_MULHU() const {
unsigned int rd, rs1, rs2;
unsigned_T multiplier, multiplicand;
unsigned_T result;
rd = this->get_rd();
rs1 = this->get_rs1();
rs2 = this->get_rs2();
multiplier = this->regs->getValue(rs1);
multiplicand = this->regs->getValue(rs2);
sc_dt::sc_bigint<128> mul = multiplier;
sc_dt::sc_bigint<128> muld = multiplicand;
sc_dt::sc_bigint<128> res = mul * muld;
result = res.range(127, 64).to_uint64();
this->regs->setValue(rd, result);
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
sc_core::sc_time_stamp().value(),
this->regs->getPC(),
rs1, rs2, rd, result);
}
}