/*! \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" SC_HAS_PROCESS(CPU); CPU::CPU(sc_core::sc_module_name name, uint32_t PC) : sc_module(name), instr_bus("instr_bus"), default_time(10, sc_core::SC_NS) { register_bank = new Registers(); mem_intf = new MemoryInterface(); perf = Performance::getInstance(); log = Log::getInstance(); register_bank->setPC(PC); //register_bank->setValue(Registers::sp, (0xD0000 / 4) - 1); register_bank->setValue(Registers::sp, (0x10000000 / 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); inst = new Instruction(0); 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); m_qk = new tlm_utils::tlm_quantumkeeper(); SC_THREAD(CPU_thread); } CPU::~CPU() { std::cout << "*********************************************" << std::endl; register_bank->dump(); std::cout << "end time: " << sc_core::sc_time_stamp() << std::endl; perf->dump(); std::cout << "*********************************************" << std::endl; delete register_bank; delete mem_intf; delete inst; delete exec; delete c_inst; delete m_inst; delete a_inst; delete m_qk; } bool CPU::cpu_process_IRQ() { uint32_t csr_temp; uint32_t new_pc, old_pc; bool ret_value = false; if (interrupt == true) { csr_temp = register_bank->getCSR(CSR_MSTATUS); if ((csr_temp & MSTATUS_MIE) == 0) { log->SC_log(Log::DEBUG) << "interrupt delayed" << std::endl; 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); log->SC_log(Log::DEBUG) << "Interrupt!" << std::endl; /* updated MEPC register */ old_pc = register_bank->getPC(); register_bank->setCSR(CSR_MEPC, old_pc); log->SC_log(Log::INFO) << "Old PC Value 0x" << std::hex << old_pc << std::endl; /* update MCAUSE register */ register_bank->setCSR(CSR_MCAUSE, 0x80000000); /* set new PC address */ new_pc = register_bank->getCSR(CSR_MTVEC); //new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0 log->SC_log(Log::DEBUG) << "NEW PC Value 0x" << std::hex << new_pc << std::endl; register_bank->setPC(new_pc); ret_value = true; interrupt = false; irq_already_down = false; } } else { if (irq_already_down == false) { 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; } void CPU::CPU_thread(void) { tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload; uint32_t INSTR; sc_core::sc_time delay = sc_core::SC_ZERO_TIME; bool PC_not_affected = false; bool incPCby2 = false; tlm::tlm_dmi dmi_data; unsigned char *dmi_ptr = NULL; 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(0); // 0 indicates unused trans->set_dmi_allowed(false); // Mandatory initial value trans->set_response_status(tlm::TLM_INCOMPLETE_RESPONSE); m_qk->reset(); while (1) { /* Get new PC value */ if (dmi_ptr_valid == true) { /* if memory_offset at Memory module is set, this won't work */ memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4); } else { 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(); log->SC_log(Log::INFO) << "PC: 0x" << std::hex << register_bank->getPC() << ". "; inst->setInstr(INSTR); /* check what type of instruction is and execute it */ switch (inst->check_extension()) { [[likely]] case BASE_EXTENSION: PC_not_affected = exec->process_instruction(inst); incPCby2 = false; break; case C_EXTENSION: PC_not_affected = c_inst->process_instruction(inst); incPCby2 = true; break; case M_EXTENSION: PC_not_affected = m_inst->process_instruction(inst); incPCby2 = false; break; case A_EXTENSION: PC_not_affected = a_inst->process_instruction(inst); incPCby2 = false; break; [[unlikely]] default: std::cout << "Extension not implemented yet" << std::endl; inst->dump(); exec->NOP(); } perf->instructionsInc(); if (PC_not_affected == true) { register_bank->incPC(incPCby2); } /* Process IRQ (if any) */ cpu_process_IRQ(); /* Fixed instruction time to 10 ns (i.e. 100 MHz) */ //#define USE_QK #ifdef USE_QK // Model time used for additional processing m_qk->inc(default_time); if (m_qk->need_sync()) { m_qk->sync(); } #else //sc_core::wait(10, sc_core::SC_NS); #endif } // while(1) } // CPU_thread void CPU::call_interrupt(tlm::tlm_generic_payload &trans, sc_core::sc_time &delay) { interrupt = true; /* Socket caller send a cause (its id) */ memcpy(&int_cause, trans.get_data_ptr(), sizeof(uint32_t)); } void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) { dmi_ptr_valid = false; }