/*! \file M_extension.h \brief Implement M extensions part of the RISC-V \author Màrius Montón \date November 2018 */ // SPDX-License-Identifier: GPL-3.0-or-later #ifndef M_EXTENSION__H #define M_EXTENSION__H #include "systemc" #include "extension_base.h" #include "Registers.h" namespace riscv_tlm { typedef enum { OP_M_MUL, OP_M_MULH, OP_M_MULHSU, OP_M_MULHU, OP_M_DIV, OP_M_DIVU, OP_M_REM, OP_M_REMU, OP_M_MULW, OP_M_DIVW, OP_M_DIVUW, OP_M_REMW, OP_M_REMUW, OP_M_ERROR } op_M_Codes; typedef enum { M_MUL = 0b000, M_MULH = 0b001, M_MULHSU = 0b010, M_MULHU = 0b011, M_DIV = 0b100, M_DIVU = 0b101, M_REM = 0b110, M_REMU = 0b111, M_MULW = 0b000, M_DIVW = 0b100, M_DIVUW = 0b101, M_REMW = 0b110, M_REMUW = 0b111, } M_Codes; /** * @brief Instruction decoding and fields access */ template class M_extension : public extension_base { public: /** * @brief Constructor, same as base class */ using extension_base::extension_base; using signed_T = typename std::make_signed::type; using unsigned_T = typename std::make_unsigned::type; /** * @brief Decodes opcode of instruction * @return opcode of instruction */ [[nodiscard]] op_M_Codes decode() const { if (this->m_instr.range(6,0) == 0b110011) { switch (opcode()) { case M_MUL: return OP_M_MUL; break; case M_MULH: return OP_M_MULH; break; case M_MULHSU: return OP_M_MULHSU; break; case M_MULHU: return OP_M_MULHU; break; case M_DIV: return OP_M_DIV; break; case M_DIVU: return OP_M_DIVU; break; case M_REM: return OP_M_REM; break; case M_REMU: return OP_M_REMU; break; [[unlikely]] default: return OP_M_ERROR; 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; } inline void dump() const override { std::cout << std::hex << "0x" << this->m_instr << std::dec << std::endl; } void Exec_M_MUL() const { unsigned int rd, rs1, rs2; signed_T multiplier, multiplicand; std::int64_t result; rd = this->get_rd(); rs1 = this->get_rs1(); rs2 = this->get_rs2(); multiplier = static_cast(this->regs->getValue(rs1)); multiplicand = static_cast(this->regs->getValue(rs2)); result = static_cast(multiplier * multiplicand); this->regs->setValue(rd, static_cast(result)); this->logger->debug("{} ns. PC: 0x{:x}. M.MUL: 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_MULH() const; void Exec_M_MULHSU() const; void Exec_M_MULHU() const; void Exec_M_DIV() const { unsigned int rd, rs1, rs2; signed_T divisor, dividend; std::int64_t result; rd = this->get_rd(); rs1 = this->get_rs1(); rs2 = this->get_rs2(); dividend = this->regs->getValue(rs1); divisor = this->regs->getValue(rs2); if (divisor == 0) { result = -1; } else if ((divisor == -1) && (dividend == static_cast(0x80000000))) { result = 0x0000000080000000; } else { result = dividend / divisor; } this->regs->setValue(rd, result); this->logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, result); } void Exec_M_DIVU() const { unsigned int rd, rs1, rs2; unsigned_T divisor, dividend; std::uint64_t result; rd = this->get_rd(); rs1 = this->get_rs1(); rs2 = this->get_rs2(); dividend = this->regs->getValue(rs1); divisor = this->regs->getValue(rs2); if (divisor == 0) { result = -1; } else { result = dividend / divisor; } this->regs->setValue(rd, result); this->logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, result); } void Exec_M_REM() const { unsigned int rd, rs1, rs2; signed_T divisor, dividend; signed_T result; rd = this->get_rd(); rs1 = this->get_rs1(); rs2 = this->get_rs2(); dividend = this->regs->getValue(rs1); divisor = this->regs->getValue(rs2); if (divisor == 0) { result = dividend; } else if ((divisor == -1) && (dividend == static_cast(0x80000000))) { result = 0; } else { result = dividend % divisor; } this->regs->setValue(rd, result); this->logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} % x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, result); } void Exec_M_REMU() const { unsigned int rd, rs1, rs2; unsigned_T divisor, dividend; unsigned_T result; rd = this->get_rd(); rs1 = this->get_rs1(); rs2 = this->get_rs2(); dividend = this->regs->getValue(rs1); divisor = this->regs->getValue(rs2); if (divisor == 0) { result = dividend; } else { result = dividend % divisor; } this->regs->setValue(rd, result); this->logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} % x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(), this->regs->getPC(), 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(this->regs->getValue(rs1) & 0x00000000FFFFFFFF); multiplicand = static_cast(this->regs->getValue(rs2) & 0x00000000FFFFFFFF); result = static_cast((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(this->regs->getValue(rs1) & 0x00000000FFFFFFFF); divisor = static_cast(this->regs->getValue(rs2) & 0x00000000FFFFFFFF); if (divisor == 0) { result = -1; } else if ((divisor == -1) && (dividend == static_cast(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(this->regs->getValue(rs1) & 0x00000000FFFFFFFF); divisor = static_cast(this->regs->getValue(rs2) & 0x00000000FFFFFFFF); if (divisor == 0) { result = -1; } else if ((divisor == 0xFFFFFFFF) && (dividend == static_cast(0x80000000))) { result = 0x0000000080000000; } else { result = static_cast(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(this->regs->getValue(rs1) & 0x00000000FFFFFFFF); divisor = static_cast(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(this->regs->getValue(rs1) & 0x00000000FFFFFFFF); divisor = static_cast(this->regs->getValue(rs2) & 0x00000000FFFFFFFF); if (divisor == 0) { result = static_cast(dividend); } else { result = static_cast(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) { this->setInstr(inst.getInstr()); switch (decode()) { case OP_M_MUL: Exec_M_MUL(); break; case OP_M_MULH: Exec_M_MULH(); break; case OP_M_MULHSU: Exec_M_MULHSU(); break; case OP_M_MULHU: Exec_M_MULHU(); break; case OP_M_DIV: Exec_M_DIV(); break; case OP_M_DIVU: Exec_M_DIVU(); break; case OP_M_REM: Exec_M_REM(); break; case OP_M_REMU: Exec_M_REMU(); 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: std::cout << "M instruction not implemented yet" << "\n"; inst.dump(); //NOP(inst); sc_core::sc_stop(); break; } return true; } private: /** * @brief Access to opcode field * @return return opcode field */ [[nodiscard]] inline unsigned_T opcode() const override { return static_cast(this->m_instr.range(14, 12)); } }; } #endif