Two instances of CPU (RV32, RV64). Need to implement RV64 specific instructions.
This commit is contained in:
parent
acf38332d5
commit
fc85c603d4
174
inc/CPU.h
174
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 {
|
||||
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<CPU> 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<spdlog::logger> 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<std::uint32_t> *getRegisterBank() { return register_bank; }
|
||||
bool CPU_step() override;
|
||||
Registers<BaseType> *getRegisterBank() { return register_bank; }
|
||||
|
||||
private:
|
||||
Registers<std::uint32_t> *register_bank;
|
||||
Performance *perf;
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
C_extension<std::uint32_t> *c_inst;
|
||||
M_extension<std::uint32_t> *m_inst;
|
||||
A_extension<std::uint32_t> *a_inst;
|
||||
BASE_ISA<std::uint32_t> *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<BaseType> *register_bank;
|
||||
C_extension<BaseType> *c_inst;
|
||||
M_extension<BaseType> *m_inst;
|
||||
A_extension<BaseType> *a_inst;
|
||||
BASE_ISA<BaseType> *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
|
||||
|
||||
/**
|
||||
* DMI pointer is not longer valid
|
||||
* @param start memory address region start
|
||||
* @param end memory address region end
|
||||
* @brief RISC_V CPU 64 bits model
|
||||
* @param name name of the module
|
||||
*/
|
||||
void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end);
|
||||
};
|
||||
class RV64 : public CPU {
|
||||
public:
|
||||
using BaseType = std::uint64_t;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param name Module name
|
||||
* @param PC Program Counter initialize value
|
||||
* @param debug To start debugging
|
||||
*/
|
||||
RV64(sc_core::sc_module_name const &name, BaseType PC, bool debug);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~RV64() override;
|
||||
|
||||
bool CPU_step() override;
|
||||
Registers<BaseType> *getRegisterBank() { return register_bank; }
|
||||
|
||||
private:
|
||||
Registers<BaseType> *register_bank;
|
||||
C_extension<BaseType> *c_inst;
|
||||
M_extension<BaseType> *m_inst;
|
||||
A_extension<BaseType> *a_inst;
|
||||
BASE_ISA<BaseType> *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
|
||||
|
|
|
@ -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]{};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
194
src/CPU.cpp
194
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<std::uint32_t>();
|
||||
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<std::uint32_t>::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<std::uint32_t>(0, register_bank, mem_intf);
|
||||
c_inst = new C_extension<std::uint32_t>(0, register_bank, mem_intf);
|
||||
m_inst = new M_extension<std::uint32_t>(0, register_bank, mem_intf);
|
||||
a_inst = new A_extension<std::uint32_t>(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<unsigned char *>(&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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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<BaseType>();
|
||||
mem_intf = new MemoryInterface();
|
||||
register_bank->setPC(PC);
|
||||
register_bank->setValue(Registers<BaseType>::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<BaseType>(0, register_bank, mem_intf);
|
||||
c_inst = new C_extension<BaseType>(0, register_bank, mem_intf);
|
||||
m_inst = new M_extension<BaseType>(0, register_bank, mem_intf);
|
||||
a_inst = new A_extension<BaseType>(0, register_bank, mem_intf);
|
||||
|
||||
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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<BaseType>();
|
||||
mem_intf = new MemoryInterface();
|
||||
register_bank->setPC(PC);
|
||||
register_bank->setValue(Registers<BaseType>::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<BaseType>(0, register_bank, mem_intf);
|
||||
c_inst = new C_extension<BaseType>(0, register_bank, mem_intf);
|
||||
m_inst = new M_extension<BaseType>(0, register_bank, mem_intf);
|
||||
a_inst = new A_extension<BaseType>(0, register_bank, mem_intf);
|
||||
|
||||
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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<std::uint32_t>::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<std::uint64_t>::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;
|
||||
}
|
||||
}
|
|
@ -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 <debuglevel> (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<spdlog::sinks::basic_file_sink_mt>("my_logger", filename);
|
||||
spdlog::filename_t log_filename = SPDLOG_FILENAME_T("newlog.txt");
|
||||
logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("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();
|
||||
|
|
Loading…
Reference in New Issue