Two instances of CPU (RV32, RV64). Need to implement RV64 specific instructions.

This commit is contained in:
Màrius Montón 2022-07-21 15:33:23 +02:00
parent acf38332d5
commit fc85c603d4
No known key found for this signature in database
GPG Key ID: FA199E7A752699F0
10 changed files with 565 additions and 237 deletions

176
inc/CPU.h
View File

@ -28,13 +28,28 @@
namespace riscv_tlm { namespace riscv_tlm {
/**
* @brief ISC_V CPU model class CPU : sc_core::sc_module {
* @param name name of the module
*/
class CPU : sc_core::sc_module {
public: 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 * @brief Instruction Memory bus socket
* @param trans transction to perfoem * @param trans transction to perfoem
@ -49,60 +64,87 @@ namespace riscv_tlm {
*/ */
tlm_utils::simple_target_socket<CPU> irq_line_socket; 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 * @brief Constructor
* @param name Module name * @param name Module name
* @param PC Program Counter initialize value * @param PC Program Counter initialize value
* @param debug To start debugging * @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 * @brief Destructor
*/ */
~CPU() override; ~RV32() override;
MemoryInterface *mem_intf; bool CPU_step() override;
Registers<BaseType> *getRegisterBank() { return register_bank; }
bool CPU_step();
Registers<std::uint32_t> *getRegisterBank() { return register_bank; }
private: private:
Registers<std::uint32_t> *register_bank; Registers<BaseType> *register_bank;
Performance *perf; C_extension<BaseType> *c_inst;
std::shared_ptr<spdlog::logger> logger; M_extension<BaseType> *m_inst;
C_extension<std::uint32_t> *c_inst; A_extension<BaseType> *a_inst;
M_extension<std::uint32_t> *m_inst; BASE_ISA<BaseType> *exec;
A_extension<std::uint32_t> *a_inst; BaseType int_cause;
BASE_ISA<std::uint32_t> *exec; BaseType INSTR;
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;
/** /**
* *
* @brief Process and triggers IRQ if all conditions met * @brief Process and triggers IRQ if all conditions met
* @return true if IRQ is triggered, false otherwise * @return true if IRQ is triggered, false otherwise
*/ */
bool cpu_process_IRQ(); bool cpu_process_IRQ() override;
/**
* main thread for CPU simulation
* @brief CPU mai thread
*/
[[noreturn]] void CPU_thread();
/** /**
* @brief callback for IRQ simple socket * @brief callback for IRQ simple socket
@ -113,14 +155,58 @@ namespace riscv_tlm {
*/ */
void call_interrupt(tlm::tlm_generic_payload &trans, void call_interrupt(tlm::tlm_generic_payload &trans,
sc_core::sc_time &delay); 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 * @brief Constructor
* @param start memory address region start * @param name Module name
* @param end memory address region end * @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<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 #endif

View File

@ -25,7 +25,7 @@ namespace riscv_tlm {
class Debug : sc_core::sc_module { class Debug : sc_core::sc_module {
public: public:
Debug(riscv_tlm::CPU *cpu, Memory *mem); Debug(riscv_tlm::RV32 *cpu, Memory *mem);
~Debug() override; ~Debug() override;
@ -41,7 +41,7 @@ namespace riscv_tlm {
static constexpr size_t bufsize = 1024 * 8; static constexpr size_t bufsize = 1024 * 8;
char iobuf[bufsize]{}; char iobuf[bufsize]{};
int conn; int conn;
riscv_tlm::CPU *dbg_cpu; riscv_tlm::RV32 *dbg_cpu;
Memory *dbg_mem; Memory *dbg_mem;
tlm::tlm_generic_payload dbg_trans; tlm::tlm_generic_payload dbg_trans;
unsigned char pyld_array[128]{}; unsigned char pyld_array[128]{};

View File

@ -407,11 +407,14 @@ namespace riscv_tlm {
Performance *perf; Performance *perf;
void initCSR();
/*
void initCSR() { void initCSR() {
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
| MISA_A_EXTENSION | MISA_I_BASE; | MISA_A_EXTENSION | MISA_I_BASE;
CSR[CSR_MSTATUS] = MISA_MXL; CSR[CSR_MSTATUS] = MISA_MXL;
} }
*/
}; };
} }
#endif #endif

View File

@ -80,7 +80,7 @@ namespace riscv_tlm {
} }
/* pure virtual functions */ /* pure virtual functions */
virtual T opcode() const = 0; virtual std::uint32_t opcode() const = 0;
virtual unsigned int get_rd() const { virtual unsigned int get_rd() const {
return m_instr.range(11, 7); return m_instr.range(11, 7);

View File

@ -11,37 +11,22 @@ namespace riscv_tlm {
SC_HAS_PROCESS(CPU); SC_HAS_PROCESS(CPU);
CPU::CPU(sc_core::sc_module_name const &name, std::uint32_t PC, bool debug) : 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) {
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();
perf = Performance::getInstance(); perf = Performance::getInstance();
logger = spdlog::get("my_logger");
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);
m_qk = new tlm_utils::tlm_quantumkeeper(); m_qk = new tlm_utils::tlm_quantumkeeper();
m_qk->reset(); 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_command(tlm::TLM_READ_COMMAND);
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&INSTR));
trans.set_data_length(4); trans.set_data_length(4);
trans.set_streaming_width(4); // = data_length to indicate no streaming trans.set_streaming_width(4); // = data_length to indicate no streaming
trans.set_byte_enable_ptr(nullptr); // 0 indicates unused trans.set_byte_enable_ptr(nullptr); // 0 indicates unused
@ -51,147 +36,12 @@ namespace riscv_tlm {
if (!debug) { if (!debug) {
SC_THREAD(CPU_thread); SC_THREAD(CPU_thread);
} }
};
logger = spdlog::get("my_logger"); void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) {
} (void) start;
(void) end;
CPU::~CPU() { dmi_ptr_valid = false;
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;
} }
[[noreturn]] void CPU::CPU_thread() { [[noreturn]] void CPU::CPU_thread() {
@ -219,18 +69,4 @@ namespace riscv_tlm {
#endif #endif
} // while(1) } // while(1)
} // CPU_thread } // 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;
}
} }

View File

@ -23,7 +23,7 @@ namespace riscv_tlm {
constexpr char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', constexpr char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; '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_cpu = cpu;
dbg_mem = mem; dbg_mem = mem;
@ -120,7 +120,7 @@ namespace riscv_tlm {
send_packet(conn, stream.str()); send_packet(conn, stream.str());
} else if (boost::starts_with(msg, "p")) { } else if (boost::starts_with(msg, "p")) {
long n = strtol(msg.c_str() + 1, 0, 16); long n = strtol(msg.c_str() + 1, 0, 16);
unsigned int reg_value; std::uint64_t reg_value;
if (n < 32) { if (n < 32) {
reg_value = register_bank->getValue(n); reg_value = register_bank->getValue(n);
} else if (n == 32) { } else if (n == 32) {

187
src/RV32.cpp Normal file
View File

@ -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;
}
}

183
src/RV64.cpp Normal file
View File

@ -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;
}
}

View File

@ -6,4 +6,21 @@
*/ */
// SPDX-License-Identifier: GPL-3.0-or-later // 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;
}
}

View File

@ -24,12 +24,16 @@
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"
typedef enum {RV32, RV64} cpu_types_t;
std::string filename; std::string filename;
bool debug_session = false; bool debug_session = false;
bool mem_dump = false; bool mem_dump = false;
uint32_t dump_addr_st = 0; uint32_t dump_addr_st = 0;
uint32_t dump_addr_end = 0; uint32_t dump_addr_end = 0;
cpu_types_t cpu_type_opt = RV32;
/** /**
* @class Simulator * @class Simulator
* This class instantiates all necessary modules, connects its ports and starts * This class instantiates all necessary modules, connects its ports and starts
@ -45,13 +49,17 @@ public:
riscv_tlm::peripherals::Trace *trace; riscv_tlm::peripherals::Trace *trace;
riscv_tlm::peripherals::Timer *timer; 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; std::uint32_t start_PC;
MainMemory = new riscv_tlm::Memory("Main_Memory", filename); MainMemory = new riscv_tlm::Memory("Main_Memory", filename);
start_PC = MainMemory->getPCfromHEX(); 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"); Bus = new riscv_tlm::BusCtrl("BusCtrl");
trace = new riscv_tlm::peripherals::Trace("Trace"); trace = new riscv_tlm::peripherals::Trace("Trace");
@ -67,7 +75,7 @@ public:
timer->irq_line.bind(cpu->irq_line_socket); timer->irq_line.bind(cpu->irq_line_socket);
if (debug_session) { if (debug_session) {
riscv_tlm::Debug debug(cpu, MainMemory); //riscv_tlm::Debug debug(cpu, MainMemory);
} }
} }
@ -83,7 +91,7 @@ public:
} }
private: private:
void MemoryDump() { void MemoryDump() const {
std::cout << "********** MEMORY DUMP ***********\n"; std::cout << "********** MEMORY DUMP ***********\n";
tlm::tlm_generic_payload trans; tlm::tlm_generic_payload trans;
tlm::tlm_dmi dmi_data; tlm::tlm_dmi dmi_data;
@ -134,8 +142,9 @@ void process_arguments(int argc, char *argv[]) {
int debug_level; int debug_level;
debug_session = false; 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) { switch (c) {
case 'D': case 'D':
debug_session = true; debug_session = true;
@ -173,6 +182,13 @@ void process_arguments(int argc, char *argv[]) {
case 'f': case 'f':
filename = std::string(optarg); filename = std::string(optarg);
break; break;
case 'R':
if (strcmp(optarg, "RV32") == 0) {
cpu_type_opt = RV32;
} else {
cpu_type_opt = RV64;
}
break;
case '?': case '?':
std::cout << "Call ./RISCV_TLM -D -L <debuglevel> (0..3) filename.hex" std::cout << "Call ./RISCV_TLM -D -L <debuglevel> (0..3) filename.hex"
<< std::endl; << std::endl;
@ -199,15 +215,15 @@ int sc_main(int argc, char *argv[]) {
/* SystemC time resolution set to 1 ns*/ /* SystemC time resolution set to 1 ns*/
sc_core::sc_set_time_resolution(1, sc_core::SC_NS); sc_core::sc_set_time_resolution(1, sc_core::SC_NS);
spdlog::filename_t filename = SPDLOG_FILENAME_T("newlog.txt"); spdlog::filename_t log_filename = SPDLOG_FILENAME_T("newlog.txt");
logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("my_logger", filename); logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("my_logger", log_filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::info); logger->set_level(spdlog::level::info);
/* Parse and process program arguments. -f is mandatory */ /* Parse and process program arguments. -f is mandatory */
process_arguments(argc, argv); process_arguments(argc, argv);
top = new Simulator("top"); top = new Simulator("top", cpu_type_opt);
auto start = std::chrono::steady_clock::now(); auto start = std::chrono::steady_clock::now();
sc_core::sc_start(); sc_core::sc_start();