diff --git a/inc/CPU.h b/inc/CPU.h index b3d7a12..c59f179 100644 --- a/inc/CPU.h +++ b/inc/CPU.h @@ -28,13 +28,28 @@ namespace riscv_tlm { -/** - * @brief ISC_V CPU model - * @param name name of the module - */ - class CPU : sc_core::sc_module { + + class CPU : sc_core::sc_module { public: + /* Constructors */ + explicit CPU(sc_core::sc_module_name const &name, bool debug); + + CPU() noexcept = delete; + CPU(const CPU& other) noexcept = delete; + CPU(CPU && other) noexcept = delete; + CPU& operator=(const CPU& other) noexcept = delete; + CPU& operator=(CPU&& other) noexcept = delete; + + /* Destructors */ + ~CPU() override = default; + + /** + * @brief Perform one instruction step + * @return Breackpoint found (TBD, always true) + */ + virtual bool CPU_step() = 0; + /** * @brief Instruction Memory bus socket * @param trans transction to perfoem @@ -49,60 +64,87 @@ namespace riscv_tlm { */ tlm_utils::simple_target_socket irq_line_socket; + /** + * @brief DMI pointer is not longer valid + * @param start memory address region start + * @param end memory address region end + */ + void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end); + + /** + * @brief CPU main thread + */ + [[noreturn]] void CPU_thread(); + + /** + * @brief Process and triggers IRQ if all conditions met + * @return true if IRQ is triggered, false otherwise + */ + virtual bool cpu_process_IRQ() = 0; + + /** + * @brief callback for IRQ simple socket + * @param trans transaction to perform (empty) + * @param delay time to annotate + * + * it triggers an IRQ when called + */ + virtual void call_interrupt(tlm::tlm_generic_payload &trans, + sc_core::sc_time &delay) = 0; + public: + MemoryInterface *mem_intf; + protected: + Performance *perf; + std::shared_ptr logger; + tlm_utils::tlm_quantumkeeper *m_qk; + Instruction inst; + bool interrupt; + bool irq_already_down; + sc_core::sc_time default_time; + bool dmi_ptr_valid; + tlm::tlm_generic_payload trans; + unsigned char *dmi_ptr = nullptr; + }; + + /** + * @brief RISC_V CPU 32 bits model + * @param name name of the module + */ + class RV32 : public CPU { + public: + using BaseType = std::uint32_t; + /** * @brief Constructor * @param name Module name * @param PC Program Counter initialize value * @param debug To start debugging */ - CPU(sc_core::sc_module_name const &name, std::uint32_t PC, bool debug); + RV32(sc_core::sc_module_name const &name, BaseType PC, bool debug); /** * @brief Destructor */ - ~CPU() override; + ~RV32() override; - MemoryInterface *mem_intf; - - bool CPU_step(); - - - Registers *getRegisterBank() { return register_bank; } + bool CPU_step() override; + Registers *getRegisterBank() { return register_bank; } private: - Registers *register_bank; - Performance *perf; - std::shared_ptr logger; - C_extension *c_inst; - M_extension *m_inst; - A_extension *a_inst; - BASE_ISA *exec; - tlm_utils::tlm_quantumkeeper *m_qk; - - Instruction inst; - bool interrupt; - std::uint32_t int_cause; - bool irq_already_down; - sc_core::sc_time default_time; - bool dmi_ptr_valid; - - tlm::tlm_generic_payload trans; - std::uint32_t INSTR; - unsigned char *dmi_ptr = nullptr; - + Registers *register_bank; + C_extension *c_inst; + M_extension *m_inst; + A_extension *a_inst; + BASE_ISA *exec; + BaseType int_cause; + BaseType INSTR; /** * * @brief Process and triggers IRQ if all conditions met * @return true if IRQ is triggered, false otherwise */ - bool cpu_process_IRQ(); - - /** - * main thread for CPU simulation - * @brief CPU mai thread - */ - [[noreturn]] void CPU_thread(); + bool cpu_process_IRQ() override; /** * @brief callback for IRQ simple socket @@ -113,14 +155,58 @@ namespace riscv_tlm { */ void call_interrupt(tlm::tlm_generic_payload &trans, sc_core::sc_time &delay); + }; // RV32 class + + /** + * @brief RISC_V CPU 64 bits model + * @param name name of the module + */ + class RV64 : public CPU { + public: + using BaseType = std::uint64_t; /** - * DMI pointer is not longer valid - * @param start memory address region start - * @param end memory address region end + * @brief Constructor + * @param name Module name + * @param PC Program Counter initialize value + * @param debug To start debugging */ - void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end); - }; + RV64(sc_core::sc_module_name const &name, BaseType PC, bool debug); + + /** + * @brief Destructor + */ + ~RV64() override; + + bool CPU_step() override; + Registers *getRegisterBank() { return register_bank; } + + private: + Registers *register_bank; + C_extension *c_inst; + M_extension *m_inst; + A_extension *a_inst; + BASE_ISA *exec; + BaseType int_cause; + BaseType INSTR; + + /** + * + * @brief Process and triggers IRQ if all conditions met + * @return true if IRQ is triggered, false otherwise + */ + bool cpu_process_IRQ() override; + + /** + * @brief callback for IRQ simple socket + * @param trans transaction to perform (empty) + * @param delay time to annotate + * + * it triggers an IRQ when called + */ + void call_interrupt(tlm::tlm_generic_payload &trans, + sc_core::sc_time &delay); + }; // RV64 class } #endif diff --git a/inc/Debug.h b/inc/Debug.h index ea838d4..390e0a9 100644 --- a/inc/Debug.h +++ b/inc/Debug.h @@ -25,7 +25,7 @@ namespace riscv_tlm { class Debug : sc_core::sc_module { public: - Debug(riscv_tlm::CPU *cpu, Memory *mem); + Debug(riscv_tlm::RV32 *cpu, Memory *mem); ~Debug() override; @@ -41,7 +41,7 @@ namespace riscv_tlm { static constexpr size_t bufsize = 1024 * 8; char iobuf[bufsize]{}; int conn; - riscv_tlm::CPU *dbg_cpu; + riscv_tlm::RV32 *dbg_cpu; Memory *dbg_mem; tlm::tlm_generic_payload dbg_trans; unsigned char pyld_array[128]{}; diff --git a/inc/Registers.h b/inc/Registers.h index bcd0b28..84a4d1b 100644 --- a/inc/Registers.h +++ b/inc/Registers.h @@ -407,11 +407,14 @@ namespace riscv_tlm { Performance *perf; + void initCSR(); + /* void initCSR() { CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION | MISA_A_EXTENSION | MISA_I_BASE; CSR[CSR_MSTATUS] = MISA_MXL; } + */ }; } #endif diff --git a/inc/extension_base.h b/inc/extension_base.h index 3abf2e6..4adb9e1 100644 --- a/inc/extension_base.h +++ b/inc/extension_base.h @@ -80,7 +80,7 @@ namespace riscv_tlm { } /* pure virtual functions */ - virtual T opcode() const = 0; + virtual std::uint32_t opcode() const = 0; virtual unsigned int get_rd() const { return m_instr.range(11, 7); diff --git a/src/CPU.cpp b/src/CPU.cpp index 6289e49..a1a00d8 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -11,37 +11,22 @@ namespace riscv_tlm { SC_HAS_PROCESS(CPU); - CPU::CPU(sc_core::sc_module_name const &name, std::uint32_t PC, bool debug) : - sc_module(name), instr_bus("instr_bus"), inst(0), - default_time(10, sc_core::SC_NS), INSTR(0) { - register_bank = new Registers(); - mem_intf = new MemoryInterface(); - + CPU::CPU(sc_core::sc_module_name const &name, bool debug) : sc_module(name), instr_bus("instr_bus"), inst(0), default_time(10, sc_core::SC_NS) { perf = Performance::getInstance(); - - register_bank->setPC(PC); - register_bank->setValue(Registers::sp, (Memory::SIZE / 4) - 1); - - irq_line_socket.register_b_transport(this, &CPU::call_interrupt); - interrupt = false; - - int_cause = 0; - irq_already_down = false; - - dmi_ptr_valid = false; - instr_bus.register_invalidate_direct_mem_ptr(this, - &CPU::invalidate_direct_mem_ptr); - - exec = new BASE_ISA(0, register_bank, mem_intf); - c_inst = new C_extension(0, register_bank, mem_intf); - m_inst = new M_extension(0, register_bank, mem_intf); - a_inst = new A_extension(0, register_bank, mem_intf); + logger = spdlog::get("my_logger"); m_qk = new tlm_utils::tlm_quantumkeeper(); m_qk->reset(); + dmi_ptr_valid = false; + + irq_already_down = false; + interrupt = false; + + irq_line_socket.register_b_transport(this, &CPU::call_interrupt); + trans.set_command(tlm::TLM_READ_COMMAND); - trans.set_data_ptr(reinterpret_cast(&INSTR)); + trans.set_data_length(4); trans.set_streaming_width(4); // = data_length to indicate no streaming trans.set_byte_enable_ptr(nullptr); // 0 indicates unused @@ -51,147 +36,12 @@ namespace riscv_tlm { if (!debug) { SC_THREAD(CPU_thread); } + }; - logger = spdlog::get("my_logger"); - } - - CPU::~CPU() { - delete register_bank; - delete mem_intf; - delete exec; - delete c_inst; - delete m_inst; - delete a_inst; - delete m_qk; - } - - bool CPU::cpu_process_IRQ() { - std::uint32_t csr_temp; - bool ret_value = false; - - if (interrupt) { - csr_temp = register_bank->getCSR(CSR_MSTATUS); - if ((csr_temp & MSTATUS_MIE) == 0) { - logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(), - register_bank->getPC()); - - return ret_value; - } - - csr_temp = register_bank->getCSR(CSR_MIP); - - if ((csr_temp & MIP_MEIP) == 0) { - csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit) - register_bank->setCSR(CSR_MIP, csr_temp); - - logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(), - register_bank->getPC()); - - - /* updated MEPC register */ - std::uint32_t old_pc = register_bank->getPC(); - register_bank->setCSR(CSR_MEPC, old_pc); - - logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(), - register_bank->getPC(), - old_pc); - - /* update MCAUSE register */ - register_bank->setCSR(CSR_MCAUSE, 0x80000000); - - /* set new PC address */ - std::uint32_t new_pc = register_bank->getCSR(CSR_MTVEC); - //new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0 - logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(), - register_bank->getPC(), - new_pc); - register_bank->setPC(new_pc); - - ret_value = true; - interrupt = false; - irq_already_down = false; - } - } else { - if (!irq_already_down) { - csr_temp = register_bank->getCSR(CSR_MIP); - csr_temp &= ~MIP_MEIP; - register_bank->setCSR(CSR_MIP, csr_temp); - irq_already_down = true; - } - } - - return ret_value; - } - - bool CPU::CPU_step() { - bool PC_not_affected = false; - - /* Get new PC value */ - if (dmi_ptr_valid) { - /* if memory_offset at Memory module is set, this won't work */ - std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4); - } else { - sc_core::sc_time delay = sc_core::SC_ZERO_TIME; - tlm::tlm_dmi dmi_data; - trans.set_address(register_bank->getPC()); - instr_bus->b_transport(trans, delay); - - if (trans.is_response_error()) { - SC_REPORT_ERROR("CPU base", "Read memory"); - } - - if (trans.is_dmi_allowed()) { - dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data); - if (dmi_ptr_valid) { - std::cout << "Get DMI_PTR " << std::endl; - dmi_ptr = dmi_data.get_dmi_ptr(); - } - } - } - - perf->codeMemoryRead(); - inst.setInstr(INSTR); - bool breakpoint = false; - - /* check what type of instruction is and execute it */ - switch (inst.check_extension()) { - [[likely]] case BASE_EXTENSION: - PC_not_affected = exec->process_instruction(inst, &breakpoint); - if (PC_not_affected) { - register_bank->incPC(); - } - break; - case C_EXTENSION: - PC_not_affected = c_inst->process_instruction(inst, &breakpoint); - if (PC_not_affected) { - register_bank->incPCby2(); - } - break; - case M_EXTENSION: - PC_not_affected = m_inst->process_instruction(inst); - if (PC_not_affected) { - register_bank->incPC(); - } - break; - case A_EXTENSION: - PC_not_affected = a_inst->process_instruction(inst); - if (PC_not_affected) { - register_bank->incPC(); - } - break; - [[unlikely]] default: - std::cout << "Extension not implemented yet" << std::endl; - inst.dump(); - exec->NOP(); - } - - if (breakpoint) { - std::cout << "Breakpoint set to true\n"; - } - - perf->instructionsInc(); - - return breakpoint; + void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) { + (void) start; + (void) end; + dmi_ptr_valid = false; } [[noreturn]] void CPU::CPU_thread() { @@ -219,18 +69,4 @@ namespace riscv_tlm { #endif } // while(1) } // CPU_thread - - void CPU::call_interrupt(tlm::tlm_generic_payload &m_trans, - sc_core::sc_time &delay) { - interrupt = true; - /* Socket caller send a cause (its id) */ - memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(std::uint32_t)); - delay = sc_core::SC_ZERO_TIME; - } - - void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) { - (void) start; - (void) end; - dmi_ptr_valid = false; - } } \ No newline at end of file diff --git a/src/Debug.cpp b/src/Debug.cpp index 51f4153..dfe4933 100644 --- a/src/Debug.cpp +++ b/src/Debug.cpp @@ -23,7 +23,7 @@ namespace riscv_tlm { constexpr char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - Debug::Debug(riscv_tlm::CPU *cpu, Memory *mem) : sc_module(sc_core::sc_module_name("Debug")) { + Debug::Debug(riscv_tlm::RV32 *cpu, Memory *mem) : sc_module(sc_core::sc_module_name("Debug")) { dbg_cpu = cpu; dbg_mem = mem; @@ -120,7 +120,7 @@ namespace riscv_tlm { send_packet(conn, stream.str()); } else if (boost::starts_with(msg, "p")) { long n = strtol(msg.c_str() + 1, 0, 16); - unsigned int reg_value; + std::uint64_t reg_value; if (n < 32) { reg_value = register_bank->getValue(n); } else if (n == 32) { diff --git a/src/RV32.cpp b/src/RV32.cpp new file mode 100644 index 0000000..f239e08 --- /dev/null +++ b/src/RV32.cpp @@ -0,0 +1,187 @@ +/*! + \file CPU.cpp + \brief Main CPU class + \author Màrius Montón + \date August 2018 + */ +// SPDX-License-Identifier: GPL-3.0-or-later +#include "CPU.h" + +namespace riscv_tlm { + + SC_HAS_PROCESS(RV32); + + RV32::RV32(sc_core::sc_module_name const &name, BaseType PC, bool debug) : + CPU(name, debug), INSTR(0) { + + register_bank = new Registers(); + mem_intf = new MemoryInterface(); + register_bank->setPC(PC); + register_bank->setValue(Registers::sp, (Memory::SIZE / 4) - 1); + + int_cause = 0; + + instr_bus.register_invalidate_direct_mem_ptr(this, + &RV32::invalidate_direct_mem_ptr); + + exec = new BASE_ISA(0, register_bank, mem_intf); + c_inst = new C_extension(0, register_bank, mem_intf); + m_inst = new M_extension(0, register_bank, mem_intf); + a_inst = new A_extension(0, register_bank, mem_intf); + + trans.set_data_ptr(reinterpret_cast(&INSTR)); + + logger->info("Created RV32 CPU"); + std::cout << "Created RV32 CPU" << std::endl; + } + + RV32::~RV32() { + delete register_bank; + delete mem_intf; + delete exec; + delete c_inst; + delete m_inst; + delete a_inst; + delete m_qk; + } + + bool RV32::cpu_process_IRQ() { + BaseType csr_temp; + bool ret_value = false; + + if (interrupt) { + csr_temp = register_bank->getCSR(CSR_MSTATUS); + if ((csr_temp & MSTATUS_MIE) == 0) { + logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(), + register_bank->getPC()); + + return ret_value; + } + + csr_temp = register_bank->getCSR(CSR_MIP); + + if ((csr_temp & MIP_MEIP) == 0) { + csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit) + register_bank->setCSR(CSR_MIP, csr_temp); + + logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(), + register_bank->getPC()); + + /* updated MEPC register */ + BaseType old_pc = register_bank->getPC(); + register_bank->setCSR(CSR_MEPC, old_pc); + + logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(), + register_bank->getPC(), + old_pc); + + /* update MCAUSE register */ + register_bank->setCSR(CSR_MCAUSE, 0x80000000); + + /* set new PC address */ + BaseType new_pc = register_bank->getCSR(CSR_MTVEC); + //new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0 + logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(), + register_bank->getPC(), + new_pc); + register_bank->setPC(new_pc); + + ret_value = true; + interrupt = false; + irq_already_down = false; + } + } else { + if (!irq_already_down) { + csr_temp = register_bank->getCSR(CSR_MIP); + csr_temp &= ~MIP_MEIP; + register_bank->setCSR(CSR_MIP, csr_temp); + irq_already_down = true; + } + } + + return ret_value; + } + + bool RV32::CPU_step() { + bool PC_not_affected = false; + + /* Get new PC value */ + if (dmi_ptr_valid) { + /* if memory_offset at Memory module is set, this won't work */ + std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4); + } else { + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + tlm::tlm_dmi dmi_data; + trans.set_address(register_bank->getPC()); + instr_bus->b_transport(trans, delay); + + if (trans.is_response_error()) { + SC_REPORT_ERROR("CPU base", "Read memory"); + } + + if (trans.is_dmi_allowed()) { + dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data); + if (dmi_ptr_valid) { + std::cout << "Get DMI_PTR " << std::endl; + dmi_ptr = dmi_data.get_dmi_ptr(); + } + } + } + + perf->codeMemoryRead(); + inst.setInstr(INSTR); + bool breakpoint = false; + + /* check what type of instruction is and execute it */ + switch (inst.check_extension()) { + [[likely]] case BASE_EXTENSION: + PC_not_affected = exec->process_instruction(inst, &breakpoint); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + case C_EXTENSION: + PC_not_affected = c_inst->process_instruction(inst, &breakpoint); + if (PC_not_affected) { + register_bank->incPCby2(); + } + break; + case M_EXTENSION: + PC_not_affected = m_inst->process_instruction(inst); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + case A_EXTENSION: + PC_not_affected = a_inst->process_instruction(inst); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + [[unlikely]] default: + std::cout << "Extension not implemented yet" << std::endl; + inst.dump(); + exec->NOP(); + } + + if (breakpoint) { + std::cout << "Breakpoint set to true\n"; + } + + perf->instructionsInc(); + + return breakpoint; + } + + + + void RV32::call_interrupt(tlm::tlm_generic_payload &m_trans, + sc_core::sc_time &delay) { + interrupt = true; + /* Socket caller send a cause (its id) */ + memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(BaseType)); + delay = sc_core::SC_ZERO_TIME; + } + + +} \ No newline at end of file diff --git a/src/RV64.cpp b/src/RV64.cpp new file mode 100644 index 0000000..ced5f80 --- /dev/null +++ b/src/RV64.cpp @@ -0,0 +1,183 @@ +/*! + \file CPU.cpp + \brief Main CPU class + \author Màrius Montón + \date August 2018 + */ +// SPDX-License-Identifier: GPL-3.0-or-later +#include "CPU.h" + +namespace riscv_tlm { + + RV64::RV64(sc_core::sc_module_name const &name, BaseType PC, bool debug) : + CPU(name, debug), INSTR(0) { + + register_bank = new Registers(); + mem_intf = new MemoryInterface(); + register_bank->setPC(PC); + register_bank->setValue(Registers::sp, (Memory::SIZE / 4) - 1); + + int_cause = 0; + + instr_bus.register_invalidate_direct_mem_ptr(this, + &RV64::invalidate_direct_mem_ptr); + + exec = new BASE_ISA(0, register_bank, mem_intf); + c_inst = new C_extension(0, register_bank, mem_intf); + m_inst = new M_extension(0, register_bank, mem_intf); + a_inst = new A_extension(0, register_bank, mem_intf); + + trans.set_data_ptr(reinterpret_cast(&INSTR)); + + logger->info("Created RV64 CPU"); + std::cout << "Created RV64 CPU" << std::endl; + } + + RV64::~RV64() { + delete register_bank; + delete mem_intf; + delete exec; + delete c_inst; + delete m_inst; + delete a_inst; + delete m_qk; + } + + bool RV64::cpu_process_IRQ() { + BaseType csr_temp; + bool ret_value = false; + + if (interrupt) { + csr_temp = register_bank->getCSR(CSR_MSTATUS); + if ((csr_temp & MSTATUS_MIE) == 0) { + logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(), + register_bank->getPC()); + + return ret_value; + } + + csr_temp = register_bank->getCSR(CSR_MIP); + + if ((csr_temp & MIP_MEIP) == 0) { + csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit) + register_bank->setCSR(CSR_MIP, csr_temp); + + logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(), + register_bank->getPC()); + + /* updated MEPC register */ + BaseType old_pc = register_bank->getPC(); + register_bank->setCSR(CSR_MEPC, old_pc); + + logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(), + register_bank->getPC(), + old_pc); + + /* update MCAUSE register */ + register_bank->setCSR(CSR_MCAUSE, 0x80000000); + + /* set new PC address */ + BaseType new_pc = register_bank->getCSR(CSR_MTVEC); + //new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0 + logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(), + register_bank->getPC(), + new_pc); + register_bank->setPC(new_pc); + + ret_value = true; + interrupt = false; + irq_already_down = false; + } + } else { + if (!irq_already_down) { + csr_temp = register_bank->getCSR(CSR_MIP); + csr_temp &= ~MIP_MEIP; + register_bank->setCSR(CSR_MIP, csr_temp); + irq_already_down = true; + } + } + + return ret_value; + } + + bool RV64::CPU_step() { + bool PC_not_affected = false; + + /* Get new PC value */ + if (dmi_ptr_valid) { + /* if memory_offset at Memory module is set, this won't work */ + std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4); + } else { + sc_core::sc_time delay = sc_core::SC_ZERO_TIME; + tlm::tlm_dmi dmi_data; + trans.set_address(register_bank->getPC()); + instr_bus->b_transport(trans, delay); + + if (trans.is_response_error()) { + SC_REPORT_ERROR("CPU base", "Read memory"); + } + + if (trans.is_dmi_allowed()) { + dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data); + if (dmi_ptr_valid) { + std::cout << "Get DMI_PTR " << std::endl; + dmi_ptr = dmi_data.get_dmi_ptr(); + } + } + } + + perf->codeMemoryRead(); + inst.setInstr(INSTR); + bool breakpoint = false; + + /* check what type of instruction is and execute it */ + switch (inst.check_extension()) { + [[likely]] case BASE_EXTENSION: + PC_not_affected = exec->process_instruction(inst, &breakpoint); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + case C_EXTENSION: + PC_not_affected = c_inst->process_instruction(inst, &breakpoint); + if (PC_not_affected) { + register_bank->incPCby2(); + } + break; + case M_EXTENSION: + PC_not_affected = m_inst->process_instruction(inst); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + case A_EXTENSION: + PC_not_affected = a_inst->process_instruction(inst); + if (PC_not_affected) { + register_bank->incPC(); + } + break; + [[unlikely]] default: + std::cout << "Extension not implemented yet" << std::endl; + inst.dump(); + exec->NOP(); + } + + if (breakpoint) { + std::cout << "Breakpoint set to true\n"; + } + + perf->instructionsInc(); + + return breakpoint; + } + + void RV64::call_interrupt(tlm::tlm_generic_payload &m_trans, + sc_core::sc_time &delay) { + interrupt = true; + /* Socket caller send a cause (its id) */ + memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(BaseType)); + delay = sc_core::SC_ZERO_TIME; + } + + +} \ No newline at end of file diff --git a/src/Registers.cpp b/src/Registers.cpp index 2b289ad..bc58468 100644 --- a/src/Registers.cpp +++ b/src/Registers.cpp @@ -6,4 +6,21 @@ */ // SPDX-License-Identifier: GPL-3.0-or-later +#include "Registers.h" +namespace riscv_tlm { + /* Specialization for each XLEN (RV32, RV64)*/ + template<> + void Registers::initCSR() { + CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION + | MISA_A_EXTENSION | MISA_I_BASE; + CSR[CSR_MSTATUS] = MISA_MXL; + } + + template<> + void Registers::initCSR() { + CSR[CSR_MISA] = (((std::uint64_t) 0x02) << 30) | MISA_M_EXTENSION | MISA_C_EXTENSION + | MISA_A_EXTENSION | MISA_I_BASE; + CSR[CSR_MSTATUS] = MISA_MXL; + } +} \ No newline at end of file diff --git a/src/Simulator.cpp b/src/Simulator.cpp index 50d81a6..973f7f4 100644 --- a/src/Simulator.cpp +++ b/src/Simulator.cpp @@ -24,12 +24,16 @@ #include "spdlog/spdlog.h" #include "spdlog/sinks/basic_file_sink.h" +typedef enum {RV32, RV64} cpu_types_t; + std::string filename; bool debug_session = false; bool mem_dump = false; uint32_t dump_addr_st = 0; uint32_t dump_addr_end = 0; +cpu_types_t cpu_type_opt = RV32; + /** * @class Simulator * This class instantiates all necessary modules, connects its ports and starts @@ -45,13 +49,17 @@ public: riscv_tlm::peripherals::Trace *trace; riscv_tlm::peripherals::Timer *timer; - explicit Simulator(sc_core::sc_module_name const &name): sc_module(name) { + explicit Simulator(sc_core::sc_module_name const &name, cpu_types_t cpu_type): sc_module(name) { std::uint32_t start_PC; MainMemory = new riscv_tlm::Memory("Main_Memory", filename); start_PC = MainMemory->getPCfromHEX(); - cpu = new riscv_tlm::CPU("cpu", start_PC, debug_session); + if (cpu_type == RV32) { + cpu = new riscv_tlm::RV32("cpu", start_PC, debug_session); + } else { + cpu = new riscv_tlm::RV64("cpu", start_PC, debug_session); + } Bus = new riscv_tlm::BusCtrl("BusCtrl"); trace = new riscv_tlm::peripherals::Trace("Trace"); @@ -67,7 +75,7 @@ public: timer->irq_line.bind(cpu->irq_line_socket); if (debug_session) { - riscv_tlm::Debug debug(cpu, MainMemory); + //riscv_tlm::Debug debug(cpu, MainMemory); } } @@ -83,7 +91,7 @@ public: } private: - void MemoryDump() { + void MemoryDump() const { std::cout << "********** MEMORY DUMP ***********\n"; tlm::tlm_generic_payload trans; tlm::tlm_dmi dmi_data; @@ -134,8 +142,9 @@ void process_arguments(int argc, char *argv[]) { int debug_level; debug_session = false; + cpu_type_opt = RV32; - while ((c = getopt(argc, argv, "DTE:B:L:f:?")) != -1) { + while ((c = getopt(argc, argv, "DTE:B:L:f:R:?")) != -1) { switch (c) { case 'D': debug_session = true; @@ -173,6 +182,13 @@ void process_arguments(int argc, char *argv[]) { case 'f': filename = std::string(optarg); break; + case 'R': + if (strcmp(optarg, "RV32") == 0) { + cpu_type_opt = RV32; + } else { + cpu_type_opt = RV64; + } + break; case '?': std::cout << "Call ./RISCV_TLM -D -L (0..3) filename.hex" << std::endl; @@ -199,15 +215,15 @@ int sc_main(int argc, char *argv[]) { /* SystemC time resolution set to 1 ns*/ sc_core::sc_set_time_resolution(1, sc_core::SC_NS); - spdlog::filename_t filename = SPDLOG_FILENAME_T("newlog.txt"); - logger = spdlog::create("my_logger", filename); + spdlog::filename_t log_filename = SPDLOG_FILENAME_T("newlog.txt"); + logger = spdlog::create("my_logger", log_filename); logger->set_pattern("%v"); logger->set_level(spdlog::level::info); /* Parse and process program arguments. -f is mandatory */ process_arguments(argc, argv); - top = new Simulator("top"); + top = new Simulator("top", cpu_type_opt); auto start = std::chrono::steady_clock::now(); sc_core::sc_start();