/*! \file C_extension.h \brief Implement C extensions part of the RISC-V \author Màrius Montón \date August 2018 */ // SPDX-License-Identifier: GPL-3.0-or-later #ifndef C_EXTENSION__H #define C_EXTENSION__H #include "systemc" #include "extension_base.h" namespace riscv_tlm { typedef enum { OP_C_ADDI4SPN, OP_C_FLD, OP_C_LW, OP_C_FLW, OP_C_LD, OP_C_FSD, OP_C_SW, OP_C_FSW, OP_C_SD, OP_C_NOP, OP_C_ADDI, OP_C_JAL, OP_C_ADDIW, OP_C_LI, OP_C_ADDI16SP, OP_C_LUI, OP_C_SRLI, OP_C_SRAI, OP_C_ANDI, OP_C_SUB, OP_C_SUBW, OP_C_XOR, OP_C_ADDW, OP_C_OR, OP_C_AND, OP_C_J, OP_C_BEQZ, OP_C_BNEZ, OP_C_SLLI, OP_C_FLDSP, OP_C_LWSP, OP_C_FLWSP, OP_C_LDSP, OP_C_JR, OP_C_MV, OP_C_EBREAK, OP_C_JALR, OP_C_ADD, OP_C_FSDSP, OP_C_SWSP, OP_C_FSWSP, OP_C_SDSP, OP_C_ERROR } op_C_Codes; typedef enum { C_ADDI4SPN = 0b000, C_FLD = 0b001, C_LW = 0b010, C_FLW = 0b011, C_FSD = 0b101, C_SW = 0b110, C_FSW = 0b111, C_ADDI = 0b000, C_JAL = 0b001, C_LI = 0b010, C_ADDI16SP = 0b011, C_SRLI = 0b100, C_2_SRLI = 0b00, C_2_SRAI = 0b01, C_2_ANDI = 0b10, C_2_SUB = 0b11, C_3_SUB = 0b00, C_3_XOR = 0b01, C_3_OR = 0b10, C_3_AND = 0b11, C_J = 0b101, C_BEQZ = 0b110, C_BNEZ = 0b111, C_SLLI = 0b000, C_FLDSP = 0b001, C_LWSP = 0b010, C_FLWSP = 0b011, C_JR = 0b100, C_FDSP = 0b101, C_SWSP = 0b110, C_FWWSP = 0b111, } C_Codes; /** * @brief Instruction decoding and fields access */ template class C_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 Access to opcode field * @return return opcode field */ [[nodiscard]] inline unsigned_T opcode() const override { return static_cast(this->m_instr.range(1, 0)); } [[nodiscard]] inline std::uint32_t get_rdp() const { return static_cast(this->m_instr.range(4, 2) + 8); } /** * @brief Access to rs1 field * @return rs1 field */ [[nodiscard]] inline std::uint32_t get_rs1() const override { return static_cast(this->m_instr.range(11, 7)); } inline void set_rs1(std::uint32_t value) override { this->m_instr.range(11, 7) = value; } [[nodiscard]] inline std::uint32_t get_rs1p() const { return static_cast(this->m_instr.range(9, 7) + 8); } /** * @brief Access to rs2 field * @return rs2 field */ [[nodiscard]] inline std::uint32_t get_rs2() const override { return static_cast(this->m_instr.range(6, 2)); } inline void set_rs2(std::uint32_t value) override { this->m_instr.range(6, 2) = value; } [[nodiscard]] inline std::uint32_t get_rs2p() const { return static_cast(this->m_instr.range(4, 2) + 8); } [[nodiscard]] inline std::uint32_t get_funct3() const override { return static_cast(this->m_instr.range(15, 13)); } inline void set_funct3(std::uint32_t value) override { this->m_instr.range(15, 13) = value; } /** * @brief Access to immediate field for I-type * @return immediate_I field */ [[nodiscard]] inline std::int32_t get_imm_I() const { std::int32_t aux = 0; aux = static_cast(this->m_instr.range(31, 20)); /* sign extension (optimize) */ if (this->m_instr[31] == 1) { aux |= (0b11111111111111111111) << 12; } return aux; } inline void set_imm_I(std::uint32_t value) { this->m_instr.range(31, 20) = value; } /** * @brief Access to immediate field for S-type * @return immediate_S field */ [[nodiscard]] inline std::int32_t get_imm_S() const { std::int32_t aux = 0; aux = static_cast(this->m_instr.range(31, 25) << 5); aux |= static_cast(this->m_instr.range(11, 7)); if (this->m_instr[31] == 1) { aux |= (0b11111111111111111111) << 12; } return aux; } inline void set_imm_S(std::uint32_t value) const { sc_dt::sc_uint<32> aux = value; this->m_instr.range(31, 25) = aux.range(11, 5); this->m_instr.range(11, 7) = aux.range(4, 0); } /** * @brief Access to immediate field for U-type * @return immediate_U field */ [[nodiscard]] inline std::uint32_t get_imm_U() const { return static_cast(this->m_instr.range(31, 12)); } inline void set_imm_U(std::uint32_t value) const { this->m_instr.range(31, 12) = (value << 12); } /** * @brief Access to immediate field for B-type * @return immediate_B field */ [[nodiscard]] inline std::int32_t get_imm_B() const { std::int32_t aux = 0; aux |= static_cast(this->m_instr[7] << 11); aux |= static_cast(this->m_instr.range(30, 25) << 5); aux |= static_cast(this->m_instr[31] << 12); aux |= static_cast(this->m_instr.range(11, 8) << 1); if (this->m_instr[31] == 1) { aux |= (0b11111111111111111111) << 12; } return aux; } inline void set_imm_B(std::uint32_t value) const { sc_dt::sc_uint<32> aux = value; this->m_instr[31] = aux[12]; this->m_instr.range(30, 25) = aux.range(10, 5); this->m_instr.range(11, 7) = aux.range(4, 1); this->m_instr[6] = aux[11]; } /** * @brief Access to immediate field for J-type * @return immediate_J field */ [[nodiscard]] inline std::int32_t get_imm_J() const { std::int32_t aux = 0; aux = static_cast(this->m_instr[12] << 11); aux |= static_cast(this->m_instr[11] << 4); aux |= static_cast(this->m_instr[10] << 9); aux |= static_cast(this->m_instr[9] << 8); aux |= static_cast(this->m_instr[8] << 10); aux |= static_cast(this->m_instr[7] << 6); aux |= static_cast(this->m_instr[6] << 7); aux |= static_cast(this->m_instr.range(5, 3) << 1); aux |= static_cast(this->m_instr[2] << 5); if (this->m_instr[12] == 1) { aux |= 0b11111111111111111111 << 12; } return aux; } inline void set_imm_J(std::uint32_t value) const { sc_dt::sc_uint<32> aux = (value << 20); this->m_instr[31] = aux[20]; this->m_instr.range(30, 21) = aux.range(10, 1); this->m_instr[20] = aux[11]; this->m_instr.range(19, 12) = aux.range(19, 12); } [[nodiscard]] inline std::uint32_t get_imm_L() const { std::uint32_t aux = 0; aux = this->m_instr.range(12, 10) << 3; aux |= this->m_instr[6] << 2; aux |= this->m_instr[5] << 6; return aux; } [[nodiscard]] inline std::uint32_t get_imm_LWSP() const { std::uint32_t aux = 0; aux = this->m_instr[12] << 5; aux |= this->m_instr.range(6, 4) << 2; aux |= this->m_instr.range(3, 2) << 6; return aux; } [[nodiscard]] inline std::uint32_t get_imm_LDSP() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr[12] << 5); aux |= static_cast(this->m_instr.range(6, 5) << 3); aux |= static_cast(this->m_instr.range(4, 2) << 6); return aux; } [[nodiscard]] inline std::int32_t get_imm_ADDI() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr[12] << 5); aux |= static_cast(this->m_instr.range(6, 2)); if (this->m_instr[12] == 1) { aux |= 0b11111111111111111111111111 << 6; } return static_cast(aux); } [[nodiscard]] inline std::uint32_t get_imm_ADDI4SPN() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr.range(12, 11) << 4); aux |= static_cast(this->m_instr.range(10, 7) << 6); aux |= static_cast(this->m_instr[6] << 2); aux |= static_cast(this->m_instr[5] << 3); return aux; } [[nodiscard]] inline std::int32_t get_imm_ADDI16SP() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr[12] << 9); aux |= static_cast(this->m_instr[6] << 4); aux |= static_cast(this->m_instr[5] << 6); aux |= static_cast(this->m_instr[4] << 8); aux |= static_cast(this->m_instr[3] << 7); aux |= static_cast(this->m_instr[2] << 5); if (this->m_instr[12] == 1) { aux |= 0b1111111111111111111111 << 10; } return aux; } [[nodiscard]] inline std::uint32_t get_imm_CSS() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr.range(12, 9) << 2); aux |= static_cast(this->m_instr.range(8, 7) << 6); return aux; } [[nodiscard]] inline std::uint32_t get_imm_CSDSP() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr.range(12, 10) << 3); aux |= static_cast(this->m_instr.range(9, 7) << 6); return aux; } [[nodiscard]] inline std::uint32_t get_imm_CB() const { std::uint32_t aux = 0; aux = static_cast(this->m_instr[12] << 8); aux |= static_cast(this->m_instr[11] << 4); aux |= static_cast(this->m_instr[10] << 3); aux |= static_cast(this->m_instr[6] << 7); aux |= static_cast(this->m_instr[5] << 6); aux |= static_cast(this->m_instr[4] << 2); aux |= static_cast(this->m_instr[3] << 1); aux |= static_cast(this->m_instr[2] << 5); if (this->m_instr[12] == 1) { aux |= 0b11111111111111111111111 << 9; } return aux; } [[nodiscard]] inline std::uint32_t get_imm_CL() const { std::uint32_t aux = 0; aux = this->m_instr.range(12, 10) << 3; aux |= this->m_instr.range(6, 5) << 6; return aux; } [[nodiscard]] inline std::int32_t get_imm_LUI() const { std::int32_t aux = 0; aux = this->m_instr[12] << 17; aux |= this->m_instr.range(6, 2) << 12; if (this->m_instr[12] == 1) { aux |= 0b111111111111111 << 17; } return aux; } [[nodiscard]] inline std::uint32_t get_csr() const { return get_imm_I(); } /** * @brief Decodes opcode of instruction * @return opcode of instruction */ [[nodiscard]] op_C_Codes decode() const { switch (opcode()) { case 0b00: switch (get_funct3()) { case C_ADDI4SPN: return OP_C_ADDI4SPN; break; case C_FLD: return OP_C_FLD; break; case C_LW: return OP_C_LW; break; case C_FLW: if (sizeof(signed_T) == 4) { // RV32 return OP_C_FLW; } else { // RV64 return OP_C_LD; } break; case C_FSD: return OP_C_FSD; break; case C_SW: return OP_C_SW; break; case C_FSW: if (sizeof(signed_T) == 4) { return OP_C_FSW; } else { return OP_C_SD; } break; [[unlikely]] default: return OP_C_ERROR; break; } break; case 0b01: switch (get_funct3()) { case C_ADDI: return OP_C_ADDI; break; case C_JAL: if (sizeof(signed_T) == 4) { return OP_C_JAL; } else { return OP_C_ADDIW; } break; case C_LI: return OP_C_LI; break; case C_ADDI16SP: return OP_C_ADDI16SP; break; case C_SRLI: switch (this->m_instr.range(11, 10)) { case C_2_SRLI: return OP_C_SRLI; break; case C_2_SRAI: return OP_C_SRAI; break; case C_2_ANDI: return OP_C_ANDI; break; case C_2_SUB: switch (this->m_instr.range(6, 5)) { case C_3_SUB: if (this->m_instr[12] == 0) { return OP_C_SUB; } else { return OP_C_SUBW; } break; case C_3_XOR: if (this->m_instr[12] == 0) { return OP_C_XOR; } else { return OP_C_ADDW; } break; case C_3_OR: return OP_C_OR; break; case C_3_AND: return OP_C_AND; break; } } break; case C_J: return OP_C_J; break; case C_BEQZ: return OP_C_BEQZ; break; case C_BNEZ: return OP_C_BNEZ; break; [[unlikely]] default: return OP_C_ERROR; break; } break; case 0b10: switch (get_funct3()) { case C_SLLI: return OP_C_SLLI; break; case C_FLDSP: case C_LWSP: return OP_C_LWSP; break; case C_FLWSP: if (sizeof(signed_T) == 4) { return OP_C_FLWSP; } else { return OP_C_LDSP; } break; case C_JR: if (this->m_instr[12] == 0) { if (this->m_instr.range(6, 2) == 0) { return OP_C_JR; } else { return OP_C_MV; } } else { if (this->m_instr.range(11, 2) == 0) { return OP_C_EBREAK; } else if (this->m_instr.range(6, 2) == 0) { return OP_C_JALR; } else { return OP_C_ADD; } } break; case C_FDSP: break; case C_SWSP: return OP_C_SWSP; break; case C_FWWSP: if (sizeof(signed_T) == 4) { return OP_C_FSWSP; } else { return OP_C_SDSP; } break; default: return OP_C_ERROR; break; } break; [[unlikely]] default: return OP_C_ERROR; break; } return OP_C_ERROR; } // PASS bool Exec_C_JR() const { std::uint32_t mem_addr; unsigned int rs1; unsigned_T new_pc; rs1 = get_rs1(); mem_addr = 0; new_pc = static_cast( static_cast((this->regs->getValue(rs1)) + static_cast(mem_addr)) & 0xFFFFFFFE); this->logger->debug("{} ns. PC: 0x{:x}. C.JR: PC <- 0x{:x}. x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), new_pc, rs1, this->regs->getValue(rs1)); this->regs->setPC(new_pc); return true; } bool Exec_C_MV() const { unsigned int rd, rs2; unsigned_T calc; rd = this->get_rd(); rs2 = get_rs2(); calc = this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.MV: x{:d}(0x{:x}) -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, this->regs->getValue(rs2), rd, calc); return true; } bool Exec_C_ADD() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1(); rs1 = get_rs1(); rs2 = get_rs2(); calc = this->regs->getValue(rs1) + this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADD: x{:d} + x{} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, calc); return true; } bool Exec_C_LWSP() const { unsigned_T mem_addr; unsigned int rd, rs1; std::uint32_t imm; unsigned_T data; // lw rd, offset[7:2](x2) rd = this->get_rd(); rs1 = 2; imm = static_cast(get_imm_LWSP()); mem_addr = imm + this->regs->getValue(rs1); data = static_cast(this->mem_intf->readDataMem(mem_addr, 4)); this->perf->dataMemoryRead(); this->regs->setValue(rd, static_cast(data)); this->regs->setValue(rd, data); this->logger->debug("{} ns. PC: 0x{:x}. C.LWSP: x{:d} + {:d}(@0x{:x}) -> x{:d}({:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, mem_addr, rd, data); return true; } bool Exec_C_ADDI4SPN() { unsigned int rd, rs1; signed_T imm; signed_T calc; rd = get_rdp(); rs1 = 2; imm = static_cast(get_imm_ADDI4SPN()); if (imm == 0) { this->RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, this->m_instr); return false; } calc = static_cast(this->regs->getValue(rs1)) + imm; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADDI4SN: x{:d} + (0x{:x}) + {:d} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, this->regs->getValue(rs1), imm, rd, calc); return true; } bool Exec_C_ADDI16SP() const { unsigned int rd; signed_T imm; if (this->get_rd() == 2) { int rs1; unsigned_T calc; rd = 2; rs1 = 2; imm = get_imm_ADDI16SP(); calc = this->regs->getValue(rs1) + imm; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADDI16SP: x{:d} + {:d} -> x{:d} (0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, rd, calc); } else { /* C.LUI OPCODE */ rd = this->get_rd(); imm = get_imm_LUI(); this->regs->setValue(rd, imm); this->logger->debug("{} ns. PC: 0x{:x}. C.LUI: x{:d} <- 0x{:x}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rd, imm); } return true; } bool Exec_C_SWSP() const { std::uint32_t mem_addr; unsigned int rs1, rs2; std::int32_t imm; std::uint32_t data; rs1 = 2; rs2 = get_rs2(); imm = static_cast(get_imm_CSS()); mem_addr = imm + this->regs->getValue(rs1); data = this->regs->getValue(rs2); this->mem_intf->writeDataMem(mem_addr, data, 4); this->perf->dataMemoryWrite(); this->logger->debug("{} ns. PC: 0x{:x}. C.SWSP: x{:d}(0x{:x}) -> x{:d} + {} (@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, data, rs1, imm, mem_addr); return true; } bool Exec_C_BEQZ() const { unsigned int rs1; unsigned_T new_pc, old_pc; unsigned_T val1; rs1 = get_rs1p(); val1 = this->regs->getValue(rs1); old_pc = this->regs->getPC(); if (val1 == 0) { new_pc = static_cast(this->regs->getPC()) + static_cast(get_imm_CB()); this->regs->setPC(new_pc); } else { this->regs->incPCby2(); new_pc = static_cast(this->regs->getPC()); } this->logger->debug("{} ns. PC: 0x{:x}. C.BEQZ: x{:d}(0x{:x}) == 0? -> PC (0xx{:d})", sc_core::sc_time_stamp().value(), old_pc, rs1, val1, new_pc); return true; } bool Exec_C_BNEZ() const { unsigned int rs1; unsigned_T new_pc, old_pc; unsigned_T val1; rs1 = get_rs1p(); val1 = this->regs->getValue(rs1); old_pc = this->regs->getPC(); if (val1 != 0) { new_pc = static_cast(this->regs->getPC()) + static_cast(get_imm_CB()); this->regs->setPC(new_pc); } else { this->regs->incPCby2(); //PC <- PC +2 new_pc = static_cast(this->regs->getPC()); } this->logger->debug("{} ns. PC: 0x{:x}. C.BNEZ: x{:d}(0x{:x}) != 0? -> PC (0x{:x})", sc_core::sc_time_stamp().value(), old_pc, rs1, val1, new_pc); return true; } bool Exec_C_LI() const { unsigned int rd, rs1; std::int32_t imm; unsigned_T calc; rd = this->get_rd(); rs1 = 0; imm = static_cast(get_imm_ADDI()); calc = this->regs->getValue(rs1) + imm; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.LI: x{:d} ({:d}) + {:d} -> x{:d}(0x{:x}) ", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, this->regs->getValue(rs1), imm, rd, calc); return true; } bool Exec_C_SRLI() const { unsigned int rd, rs1, rs2; std::uint32_t shift; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_imm_ADDI(); shift = rs2; calc = static_cast(this->regs->getValue(rs1)) >> shift; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.SRLI: x{:d} >> {} -> x{:d}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, shift, rd); return true; } bool Exec_C_SRAI() const { unsigned int rd, rs1, rs2; std::uint32_t shift; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_imm_ADDI(); shift = rs2; calc = static_cast(this->regs->getValue(rs1)) >> shift; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.SRAI: x{:d} >> {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, shift, rd, calc); return true; } bool Exec_C_SLLI() const { unsigned int rd, rs1; unsigned_T shift; unsigned_T calc; rd = get_rs1(); rs1 = get_rs1(); shift = get_imm_ADDI(); calc = this->regs->getValue(rs1) << shift; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.SLLI: x{:d} << {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, shift, rd, calc); return true; } bool Exec_C_ANDI() const { unsigned int rd, rs1; signed_T imm; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); imm = get_imm_ADDI(); calc = this->regs->getValue(rs1) & imm; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. ANDI: x{:d} C.AND 0x{:x} -> x{:d}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, rd); return true; } bool Exec_C_SUB() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = this->regs->getValue(rs1) - this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.SUB: x{:d} - x{:d} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, calc); return true; } bool Exec_C_SUBW() const { unsigned int rd, rs1, rs2; std::uint32_t calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = static_cast((this->regs->getValue(rs1) - this->regs->getValue(rs2)) & 0xFFFFFFFF); this->regs->setValue(rd, static_cast(calc)); this->logger->debug("{} ns. PC: 0x{:x}. C.SUBW: x{:d} - x{:d} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, calc); return true; } bool Exec_C_ADDW() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = static_cast((this->regs->getValue(rs1) + this->regs->getValue(rs2)) & 0xFFFFFFFF); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADDW: x{:d} + x{} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd, calc); return true; } bool Exec_C_SDSP() const { unsigned_T mem_addr; unsigned int rs1, rs2; signed_T imm; std::uint64_t data; rs1 = 2; rs2 = get_rs2(); imm = get_imm_CSDSP(); mem_addr = imm + this->regs->getValue(rs1); data = this->regs->getValue(rs2); this->logger->debug("{} ns. PC: 0x{:x}. C.SDSP: 0x{:x} -> x{:d} + 0x{:x}(@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, rs1, imm, mem_addr); this->mem_intf->writeDataMem(mem_addr, data & 0xFFFFFFFF, 4); this->mem_intf->writeDataMem(mem_addr + 4, data >> 32, 4); this->perf->dataMemoryWrite(); return true; } bool Exec_C_XOR() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = this->regs->getValue(rs1) ^ this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.XOR: x{:d} XOR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd); return true; } bool Exec_C_OR() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = this->regs->getValue(rs1) | this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.OR: x{:d} OR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd); return true; } bool Exec_C_AND() const { unsigned int rd, rs1, rs2; unsigned_T calc; rd = get_rs1p(); rs1 = get_rs1p(); rs2 = get_rs2p(); calc = this->regs->getValue(rs1) & this->regs->getValue(rs2); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.AND: x{:d} AND x{:d} -> x{:d}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, rs2, rd); return true; } bool Exec_C_ADDI() const { unsigned int rd, rs1; signed_T imm; unsigned_T calc; rd = this->get_rd(); rs1 = rd; imm = static_cast(get_imm_ADDI()); calc = this->regs->getValue(rs1) + imm; this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADDI: x{:d} + {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, rd, calc); return true; } bool Exec_C_ADDIW() const { unsigned int rd, rs1; std::int32_t imm; std::int32_t aux; std::int64_t calc; rd = this->get_rd(); rs1 = rd; imm = get_imm_ADDI(); aux = static_cast(this->regs->getValue(rs1) & 0xFFFFFFFF); aux = static_cast(aux + imm); calc = static_cast(aux); this->regs->setValue(rd, calc); this->logger->debug("{} ns. PC: 0x{:x}. C.ADDIW: x{:d} + {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, rd, calc); return true; } bool Exec_C_JALR() const { std::uint32_t mem_addr = 0; unsigned int rd, rs1; std::uint32_t new_pc, old_pc; rd = 1; rs1 = get_rs1(); old_pc = static_cast(this->regs->getPC()); new_pc = static_cast((this->regs->getValue(rs1) + mem_addr) & 0xFFFFFFFE); this->regs->setValue(rd, old_pc + 2); this->regs->setPC(new_pc); this->logger->debug("{} ns. PC: 0x{:x}. C.JALR: x{:d} <- 0x{:x} PC <- 0xx{:x}", sc_core::sc_time_stamp().value(), this->regs->getPC(), rd, old_pc + 4, new_pc); return true; } bool Exec_C_LW() const { unsigned_T mem_addr; unsigned int rd, rs1; unsigned_T imm; signed_T data; rd = get_rdp(); rs1 = get_rs1p(); imm = get_imm_L(); mem_addr = imm + this->regs->getValue(rs1); data = static_cast(this->mem_intf->readDataMem(mem_addr, 4)); this->perf->dataMemoryRead(); this->regs->setValue(rd, data); this->logger->debug("{} ns. PC: 0x{:x}. C.LW: x{:d}(0x{:x}) + {:d} (@0x{:x}) -> {:d} (0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, this->regs->getValue(rs1), imm, mem_addr, rd, data); return true; } bool Exec_C_LD() const { unsigned_T mem_addr; unsigned int rd, rs1; unsigned_T imm; std::uint64_t data; rd = get_rdp(); rs1 = get_rs1p(); imm = get_imm_CL(); mem_addr = imm + this->regs->getValue(rs1); data = static_cast(this->mem_intf->readDataMem(mem_addr, 4)); std::uint64_t aux = static_cast(this->mem_intf->readDataMem(mem_addr + 4, 4)); data |= aux << 32; this->perf->dataMemoryRead(); this->regs->setValue(rd, data); this->logger->debug("{} ns. PC: 0x{:x}. C.LD: 0x{:x} + x{:d} (0x{:x}) -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs1, imm, mem_addr, rd, data); return true; } bool Exec_C_SD() const { unsigned_T mem_addr; unsigned int rs1, rs2; signed_T imm; std::uint64_t data; rs1 = get_rs1p(); rs2 = get_rs2p(); imm = get_imm_CL(); mem_addr = imm + this->regs->getValue(rs1); data = this->regs->getValue(rs2); this->logger->debug("{} ns. PC: 0x{:x}. C.SD: 0x{:x} -> x{:d} + 0x{:x}(@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, rs1, imm, mem_addr); this->mem_intf->writeDataMem(mem_addr, data & 0xFFFFFFFF, 4); this->mem_intf->writeDataMem(mem_addr + 4, data >> 32, 4); this->perf->dataMemoryWrite(); return true; } bool Exec_C_SW() const { std::uint32_t mem_addr; unsigned int rs1, rs2; std::int32_t imm; std::uint32_t data; rs1 = get_rs1p(); rs2 = get_rs2p(); imm = get_imm_L(); mem_addr = imm + this->regs->getValue(rs1); data = this->regs->getValue(rs2); this->mem_intf->writeDataMem(mem_addr, data, 4); this->perf->dataMemoryWrite(); this->logger->debug("{} ns. PC: 0x{:x}. C.SW: x{:d}(0x{:x}) -> x{:d} + 0x{:x}(@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, data, rs1, imm, mem_addr); return true; } bool Exec_C_FSW_SD() const { std::uint32_t mem_addr; unsigned int rs1, rs2; std::int32_t imm; std::uint64_t data; std::uint32_t aux; rs1 = get_rs1p(); rs2 = get_rs2p(); imm = get_imm_L(); mem_addr = imm + this->regs->getValue(rs1); data = this->regs->getValue(rs2); aux = data >> 32; this->mem_intf->writeDataMem(mem_addr, aux, 4); aux = data & 0x00000000FFFFFFFF; this->mem_intf->writeDataMem(mem_addr+4, aux, 4); this->perf->dataMemoryWrite(); this->logger->debug("{} ns. PC: 0x{:x}. C.SD: x{:d}(0x{:x}) -> x{:d} + 0x{:x}(@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), rs2, data, rs1, imm, mem_addr); return true; } // Implemented RV64 C.LDSP only (FLWSP is for RV32 with F extension) bool Exec_C_LDSP() const { unsigned_T mem_addr; unsigned int rd, rs1; unsigned_T imm; std::uint64_t data; std::uint64_t aux; rd = this->get_rd(); rs1 = 2; imm = get_imm_LDSP(); if (rd == 0) { // Error std::cout << "C.LDSP Error!\n"; } mem_addr = imm + this->regs->getValue(rs1); data = static_cast(this->mem_intf->readDataMem(mem_addr, 4)); aux = static_cast(this->mem_intf->readDataMem(mem_addr + 4, 4)); data |= aux << 32; this->perf->dataMemoryRead(); this->regs->setValue(rd, data); this->logger->debug("{} ns. PC: 0x{:x}. C.LDSP: x{:d}(0x{:x}) -> x{:d} + 0x{:x}(@0x{:x})", sc_core::sc_time_stamp().value(), this->regs->getPC(), 2, data, rs1, imm, mem_addr); return true; } bool Exec_C_JAL(int m_rd) const { std::int32_t mem_addr; unsigned int rd; std::uint32_t new_pc, old_pc; rd = m_rd; mem_addr = get_imm_J(); old_pc = static_cast(this->regs->getPC()); new_pc = old_pc + mem_addr; this->regs->setPC(new_pc); old_pc = old_pc + 2; this->regs->setValue(rd, old_pc); this->logger->debug("{} ns. PC: 0x{:x}. C.JAL: x{:d} <- 0x{:x}. PC + 0x{:x} -> PC (0x{:x})", sc_core::sc_time_stamp().value(), old_pc - 2, rd, old_pc, mem_addr, new_pc); return true; } bool Exec_C_EBREAK() const { this->logger->debug("C.EBREAK"); std::cout << "\n" << "C.EBRAK Instruction called, dumping information" << "\n"; this->regs->dump(); std::cout << "Simulation time " << sc_core::sc_time_stamp() << "\n"; this->perf->dump(); sc_core::sc_stop(); return true; } bool process_instruction(Instruction &inst, bool *breakpoint) { bool PC_not_affected = true; *breakpoint = false; this->setInstr(inst.getInstr()); switch (decode()) { case OP_C_ADDI4SPN: PC_not_affected = Exec_C_ADDI4SPN(); break; case OP_C_LW: Exec_C_LW(); break; case OP_C_SW: Exec_C_SW(); break; case OP_C_ADDI: Exec_C_ADDI(); break; case OP_C_JAL: Exec_C_JAL(1); PC_not_affected = false; break; case OP_C_J: Exec_C_JAL(0); PC_not_affected = false; break; case OP_C_LI: Exec_C_LI(); break; case OP_C_SLLI: Exec_C_SLLI(); break; case OP_C_LWSP: Exec_C_LWSP(); break; case OP_C_JR: Exec_C_JR(); PC_not_affected = false; break; case OP_C_MV: Exec_C_MV(); break; case OP_C_JALR: Exec_C_JALR(); PC_not_affected = false; break; case OP_C_ADD: Exec_C_ADD(); break; case OP_C_SWSP: Exec_C_SWSP(); break; case OP_C_ADDI16SP: Exec_C_ADDI16SP(); break; case OP_C_BEQZ: Exec_C_BEQZ(); PC_not_affected = false; break; case OP_C_BNEZ: Exec_C_BNEZ(); PC_not_affected = false; break; case OP_C_SRLI: Exec_C_SRLI(); break; case OP_C_SRAI: Exec_C_SRAI(); break; case OP_C_ANDI: Exec_C_ANDI(); break; case OP_C_SUB: Exec_C_SUB(); break; case OP_C_XOR: Exec_C_XOR(); break; case OP_C_OR: Exec_C_OR(); break; case OP_C_AND: Exec_C_AND(); break; case OP_C_FSW: Exec_C_FSW_SD(); break; case OP_C_FLWSP: //Exec_C_FLWSP_LDSP(); break; case OP_C_EBREAK: Exec_C_EBREAK(); std::cout << "C_EBREAK" << std::endl; *breakpoint = true; break; case OP_C_LD: Exec_C_LD(); break; case OP_C_SD: Exec_C_SD(); break; case OP_C_ADDIW: Exec_C_ADDIW(); break; case OP_C_ADDW: Exec_C_ADDW(); break; case OP_C_SUBW: Exec_C_SUBW(); break; case OP_C_SDSP: Exec_C_SDSP(); break; case OP_C_LDSP: Exec_C_LDSP(); break; [[unlikely]] default: std::cout << "C instruction not implemented yet" << "\n"; inst.dump(); this->NOP(); break; } return PC_not_affected; } }; } #endif