Changed to template classe to prepare for 64bits version
This commit is contained in:
parent
d1fa3c752e
commit
10ed1fa653
|
@ -52,59 +52,465 @@ namespace riscv_tlm {
|
|||
/**
|
||||
* @brief Instruction decoding and fields access
|
||||
*/
|
||||
class A_extension : public extension_base<std::uint32_t> {
|
||||
template<typename T>
|
||||
class A_extension : public extension_base<T> {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor, same as base class
|
||||
*/
|
||||
using extension_base::extension_base;
|
||||
using extension_base<T>::extension_base;
|
||||
|
||||
/**
|
||||
* @brief Access to opcode field
|
||||
* @return return opcode field
|
||||
*/
|
||||
inline int32_t opcode() const override {
|
||||
return static_cast<int32_t>(m_instr.range(31, 27));
|
||||
inline std::uint32_t opcode() const override {
|
||||
return static_cast<std::uint32_t>(this->m_instr.range(31, 27));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decodes opcode of instruction
|
||||
* @return opcode of instruction
|
||||
*/
|
||||
op_A_Codes decode() const;
|
||||
op_A_Codes decode() const {
|
||||
|
||||
inline void dump() const override {
|
||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
||||
switch (opcode()) {
|
||||
case A_LR:
|
||||
return OP_A_LR;
|
||||
break;
|
||||
case A_SC:
|
||||
return OP_A_SC;
|
||||
break;
|
||||
case A_AMOSWAP:
|
||||
return OP_A_AMOSWAP;
|
||||
break;
|
||||
case A_AMOADD:
|
||||
return OP_A_AMOADD;
|
||||
break;
|
||||
case A_AMOXOR:
|
||||
return OP_A_AMOXOR;
|
||||
break;
|
||||
case A_AMOAND:
|
||||
return OP_A_AMOAND;
|
||||
break;
|
||||
case A_AMOOR:
|
||||
return OP_A_AMOOR;
|
||||
break;
|
||||
case A_AMOMIN:
|
||||
return OP_A_AMOMIN;
|
||||
break;
|
||||
case A_AMOMAX:
|
||||
return OP_A_AMOMAX;
|
||||
break;
|
||||
case A_AMOMINU:
|
||||
return OP_A_AMOMINU;
|
||||
break;
|
||||
case A_AMOMAXU:
|
||||
return OP_A_AMOMAXU;
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
return OP_A_ERROR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return OP_A_ERROR;
|
||||
}
|
||||
|
||||
bool Exec_A_LR();
|
||||
inline void dump() const override {
|
||||
std::cout << std::hex << "0x" << this->m_instr << std::dec << std::endl;
|
||||
}
|
||||
|
||||
bool Exec_A_SC();
|
||||
bool Exec_A_LR() {
|
||||
std::uint32_t mem_addr = 0;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
bool Exec_A_AMOSWAP() const;
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
bool Exec_A_AMOADD() const;
|
||||
if (rs2 != 0) {
|
||||
std::cout << "ILEGAL INSTRUCTION, LR.W: rs2 != 0" << std::endl;
|
||||
this->RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, this->m_instr);
|
||||
|
||||
bool Exec_A_AMOXOR() const;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOAND() const;
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
bool Exec_A_AMOOR() const;
|
||||
TLB_reserve(mem_addr);
|
||||
|
||||
bool Exec_A_AMOMIN() const;
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.LR.W: x{:d}(0x{:x}) -> x{:d}(0x{:x}) ",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, mem_addr, rd, data);
|
||||
|
||||
bool Exec_A_AMOMAX() const;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOMINU() const;
|
||||
bool Exec_A_SC() {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
bool Exec_A_AMOMAXU() const;
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
bool process_instruction(Instruction &inst);
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->regs->getValue(rs2);
|
||||
|
||||
void TLB_reserve(std::uint32_t address);
|
||||
if (TLB_reserved(mem_addr)) {
|
||||
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
this->regs->setValue(rd, 0); // SC writes 0 to rd on success
|
||||
} else {
|
||||
this->regs->setValue(rd, 1); // SC writes nonzero on failure
|
||||
}
|
||||
|
||||
bool TLB_reserved(std::uint32_t address);
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.SC.W: (0x{:x}) <- x{:d}(0x{:x}) ",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
mem_addr, rs2, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOSWAP() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// swap
|
||||
aux = this->regs->getValue(rs2);
|
||||
this->regs->setValue(rs2, static_cast<int32_t>(data));
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOSWAP");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOADD() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data + this->regs->getValue(rs2);
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOADD");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOXOR() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data ^ this->regs->getValue(rs2);
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOXOR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOAND() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data & this->regs->getValue(rs2);
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOAND");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOOR() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data | this->regs->getValue(rs2);
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOOR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOMIN() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// min
|
||||
aux = this->regs->getValue(rs2);
|
||||
if ((int32_t) data < (int32_t) aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMIN");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOMAX() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// >
|
||||
aux = this->regs->getValue(rs2);
|
||||
if ((int32_t) data > (int32_t) aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMAX");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOMINU() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// min
|
||||
aux = this->regs->getValue(rs2);
|
||||
if (data < aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMINU");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exec_A_AMOMAXU() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
mem_addr = this->regs->getValue(rs1);
|
||||
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||
this->perf->dataMemoryRead();
|
||||
|
||||
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// max
|
||||
aux = this->regs->getValue(rs2);
|
||||
if (data > aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
this->perf->dataMemoryWrite();
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMAXU");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TLB_reserve(std::uint32_t address) {
|
||||
TLB_A_Entries.insert(address);
|
||||
}
|
||||
|
||||
bool TLB_reserved(std::uint32_t address) {
|
||||
if (TLB_A_Entries.count(address) == 1) {
|
||||
TLB_A_Entries.erase(address);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool process_instruction(Instruction &inst) {
|
||||
bool PC_not_affected = true;
|
||||
|
||||
this->setInstr(inst.getInstr());
|
||||
|
||||
switch (decode()) {
|
||||
case OP_A_LR:
|
||||
Exec_A_LR();
|
||||
break;
|
||||
case OP_A_SC:
|
||||
Exec_A_SC();
|
||||
break;
|
||||
case OP_A_AMOSWAP:
|
||||
Exec_A_AMOSWAP();
|
||||
break;
|
||||
case OP_A_AMOADD:
|
||||
Exec_A_AMOADD();
|
||||
break;
|
||||
case OP_A_AMOXOR:
|
||||
Exec_A_AMOXOR();
|
||||
break;
|
||||
case OP_A_AMOAND:
|
||||
Exec_A_AMOAND();
|
||||
break;
|
||||
case OP_A_AMOOR:
|
||||
Exec_A_AMOOR();
|
||||
break;
|
||||
case OP_A_AMOMIN:
|
||||
Exec_A_AMOMIN();
|
||||
break;
|
||||
case OP_A_AMOMAX:
|
||||
Exec_A_AMOMAX();
|
||||
break;
|
||||
case OP_A_AMOMINU:
|
||||
Exec_A_AMOMINU();
|
||||
break;
|
||||
case OP_A_AMOMAXU:
|
||||
Exec_A_AMOMAXU();
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
std::cout << "A instruction not implemented yet" << std::endl;
|
||||
inst.dump();
|
||||
this->NOP();
|
||||
break;
|
||||
}
|
||||
|
||||
return PC_not_affected;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_set<std::uint32_t> TLB_A_Entries;
|
||||
|
|
1580
inc/BASE_ISA.h
1580
inc/BASE_ISA.h
File diff suppressed because it is too large
Load Diff
|
@ -73,10 +73,10 @@ namespace riscv_tlm {
|
|||
Registers<std::uint32_t> *register_bank;
|
||||
Performance *perf;
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
C_extension *c_inst;
|
||||
M_extension *m_inst;
|
||||
A_extension *a_inst;
|
||||
BASE_ISA *exec;
|
||||
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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,41 +43,298 @@ namespace riscv_tlm {
|
|||
/**
|
||||
* @brief Instruction decoding and fields access
|
||||
*/
|
||||
class M_extension : public extension_base<std::uint32_t> {
|
||||
template<typename T>
|
||||
class M_extension : public extension_base<T> {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor, same as base clase
|
||||
*/
|
||||
using extension_base::extension_base;
|
||||
using extension_base<T>::extension_base;
|
||||
|
||||
/**
|
||||
* @brief Decodes opcode of instruction
|
||||
* @return opcode of instruction
|
||||
*/
|
||||
op_M_Codes decode() const;
|
||||
[[nodiscard]] op_M_Codes decode() const {
|
||||
|
||||
inline void dump() const override {
|
||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
||||
switch (opcode()) {
|
||||
case M_MUL:
|
||||
return OP_M_MUL;
|
||||
break;
|
||||
case M_MULH:
|
||||
return OP_M_MULH;
|
||||
break;
|
||||
case M_MULHSU:
|
||||
return OP_M_MULHSU;
|
||||
break;
|
||||
case M_MULHU:
|
||||
return OP_M_MULHU;
|
||||
break;
|
||||
case M_DIV:
|
||||
return OP_M_DIV;
|
||||
break;
|
||||
case M_DIVU:
|
||||
return OP_M_DIVU;
|
||||
break;
|
||||
case M_REM:
|
||||
return OP_M_REM;
|
||||
break;
|
||||
case M_REMU:
|
||||
return OP_M_REMU;
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
return OP_M_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return OP_M_ERROR;
|
||||
}
|
||||
|
||||
bool Exec_M_MUL() const;
|
||||
inline void dump() const override {
|
||||
std::cout << std::hex << "0x" << this->m_instr << std::dec << std::endl;
|
||||
}
|
||||
|
||||
bool Exec_M_MULH() const;
|
||||
void Exec_M_MUL() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::int32_t multiplier, multiplicand;
|
||||
std::int64_t result;
|
||||
|
||||
bool Exec_M_MULHSU() const;
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
bool Exec_M_MULHU() const;
|
||||
multiplier = static_cast<std::int32_t>(extension_base<T>::regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(extension_base<T>::regs->getValue(rs2));
|
||||
|
||||
bool Exec_M_DIV() const;
|
||||
result = static_cast<std::int64_t>(multiplier * multiplicand);
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
bool Exec_M_DIVU() const;
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d} * x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
extension_base<T>::regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
bool Exec_M_REM() const;
|
||||
void Exec_M_MULH() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::int32_t multiplier, multiplicand;
|
||||
std::int64_t result;
|
||||
std::int32_t ret_value;
|
||||
|
||||
bool Exec_M_REMU() const;
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
bool process_instruction(Instruction &inst);
|
||||
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||
|
||||
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
|
||||
|
||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||
this->regs->setValue(rd, ret_value);
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d} * x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_MULHSU() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::int32_t multiplier;
|
||||
std::uint32_t multiplicand;
|
||||
std::int64_t result;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
multiplicand = this->regs->getValue(rs2);
|
||||
|
||||
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
|
||||
result = (result >> 32) & 0x00000000FFFFFFFF;
|
||||
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_MULHU() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::uint32_t multiplier, multiplicand;
|
||||
std::uint64_t result;
|
||||
std::int32_t ret_value;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||
|
||||
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
|
||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||
this->regs->setValue(rd, ret_value);
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_DIV() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::int32_t divisor, dividend;
|
||||
std::int64_t result;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = -1;
|
||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||
result = 0x0000000080000000;
|
||||
} else {
|
||||
result = dividend / divisor;
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
}
|
||||
|
||||
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_DIVU() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::uint32_t divisor, dividend;
|
||||
std::uint64_t result;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
dividend = this->regs->getValue(rs1);
|
||||
divisor = this->regs->getValue(rs2);
|
||||
|
||||
if (divisor == 0) {
|
||||
result = -1;
|
||||
} else {
|
||||
result = dividend / divisor;
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
}
|
||||
|
||||
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_REM() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::int32_t divisor, dividend;
|
||||
std::int32_t result;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = dividend;
|
||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = dividend % divisor;
|
||||
}
|
||||
|
||||
this->regs->setValue(rd, result);
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} / x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
void Exec_M_REMU() const {
|
||||
unsigned int rd, rs1, rs2;
|
||||
std::uint32_t divisor, dividend;
|
||||
std::uint32_t result;
|
||||
|
||||
rd = this->get_rd();
|
||||
rs1 = this->get_rs1();
|
||||
rs2 = this->get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = dividend;
|
||||
} else {
|
||||
result = dividend % divisor;
|
||||
}
|
||||
|
||||
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
this->logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} / x{:d} -> x{:d}({:d})",
|
||||
sc_core::sc_time_stamp().value(),
|
||||
this->regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
}
|
||||
|
||||
bool process_instruction(Instruction &inst) {
|
||||
this->setInstr(inst.getInstr());
|
||||
|
||||
switch (decode()) {
|
||||
case OP_M_MUL:
|
||||
Exec_M_MUL();
|
||||
break;
|
||||
case OP_M_MULH:
|
||||
Exec_M_MULH();
|
||||
break;
|
||||
case OP_M_MULHSU:
|
||||
Exec_M_MULHSU();
|
||||
break;
|
||||
case OP_M_MULHU:
|
||||
Exec_M_MULHU();
|
||||
break;
|
||||
case OP_M_DIV:
|
||||
Exec_M_DIV();
|
||||
break;
|
||||
case OP_M_DIVU:
|
||||
Exec_M_DIVU();
|
||||
break;
|
||||
case OP_M_REM:
|
||||
Exec_M_REM();
|
||||
break;
|
||||
case OP_M_REMU:
|
||||
Exec_M_REMU();
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
std::cout << "M instruction not implemented yet" << "\n";
|
||||
inst.dump();
|
||||
//NOP(inst);
|
||||
sc_core::sc_stop();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
@ -85,8 +342,8 @@ namespace riscv_tlm {
|
|||
* @brief Access to opcode field
|
||||
* @return return opcode field
|
||||
*/
|
||||
inline std::int32_t opcode() const override {
|
||||
return static_cast<std::int32_t>(m_instr.range(14, 12));
|
||||
[[nodiscard]] inline std::uint32_t opcode() const override {
|
||||
return static_cast<std::uint32_t>(this->m_instr.range(14, 12));
|
||||
}
|
||||
|
||||
};
|
||||
|
|
251
inc/Registers.h
251
inc/Registers.h
|
@ -10,6 +10,7 @@
|
|||
#define REGISTERS_H
|
||||
|
||||
#define SC_INCLUDE_DYNAMIC_PROCESSES
|
||||
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -188,11 +189,11 @@ namespace riscv_tlm {
|
|||
* Default constructor
|
||||
*/
|
||||
Registers() {
|
||||
perf = Performance::getInstance();
|
||||
perf = Performance::getInstance();
|
||||
|
||||
initCSR();
|
||||
register_bank[sp] = Memory::SIZE - 4; // default stack at the end of the memory
|
||||
register_PC = 0x80000000; // default _start address
|
||||
initCSR();
|
||||
register_bank[sp] = Memory::SIZE - 4; // default stack at the end of the memory
|
||||
register_PC = 0x80000000; // default _start address
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -200,10 +201,10 @@ namespace riscv_tlm {
|
|||
* @param reg_num register number
|
||||
* @param value register value
|
||||
*/
|
||||
void setValue(int reg_num, T value) {
|
||||
if ((reg_num != 0) && (reg_num < 32)) {
|
||||
register_bank[reg_num] = value;
|
||||
perf->registerWrite();
|
||||
void setValue(unsigned int reg_num, T value) {
|
||||
if ((reg_num != 0) && (reg_num < 32)) {
|
||||
register_bank[reg_num] = value;
|
||||
perf->registerWrite();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,13 +213,13 @@ namespace riscv_tlm {
|
|||
* @param reg_num register number
|
||||
* @return register value
|
||||
*/
|
||||
T getValue(int reg_num) const {
|
||||
if ((reg_num >= 0) && (reg_num < 32)) {
|
||||
perf->registerRead();
|
||||
return register_bank[reg_num];
|
||||
T getValue(unsigned int reg_num) const {
|
||||
if (reg_num < 32) {
|
||||
perf->registerRead();
|
||||
return register_bank[reg_num];
|
||||
} else {
|
||||
/* TODO Exten sign for any possible T type */
|
||||
return static_cast<T>(0xFFFFFFFF);
|
||||
/* Extend sign for any possible T type */
|
||||
return static_cast<T>(std::numeric_limits<T>::max());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +228,7 @@ namespace riscv_tlm {
|
|||
* @return PC value
|
||||
*/
|
||||
T getPC() const {
|
||||
return register_PC;
|
||||
return register_PC;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,7 +236,7 @@ namespace riscv_tlm {
|
|||
* @param new_pc new address to PC
|
||||
*/
|
||||
void setPC(T new_pc) {
|
||||
register_PC = new_pc;
|
||||
register_PC = new_pc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,40 +256,40 @@ namespace riscv_tlm {
|
|||
* @return CSR value
|
||||
*/
|
||||
T getCSR(int csr) {
|
||||
T ret_value;
|
||||
T ret_value;
|
||||
|
||||
switch (csr) {
|
||||
case CSR_CYCLE:
|
||||
case CSR_MCYCLE:
|
||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
& 0x00000000FFFFFFFF;
|
||||
break;
|
||||
case CSR_CYCLEH:
|
||||
case CSR_MCYCLEH:
|
||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
>> 32 & 0x00000000FFFFFFFF);
|
||||
break;
|
||||
case CSR_TIME:
|
||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
& 0x00000000FFFFFFFF;
|
||||
break;
|
||||
case CSR_TIMEH:
|
||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
>> 32 & 0x00000000FFFFFFFF);
|
||||
break;
|
||||
[[likely]] default:
|
||||
ret_value = CSR[csr];
|
||||
break;
|
||||
switch (csr) {
|
||||
case CSR_CYCLE:
|
||||
case CSR_MCYCLE:
|
||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
& 0x00000000FFFFFFFF;
|
||||
break;
|
||||
case CSR_CYCLEH:
|
||||
case CSR_MCYCLEH:
|
||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
>> 32 & 0x00000000FFFFFFFF);
|
||||
break;
|
||||
case CSR_TIME:
|
||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
& 0x00000000FFFFFFFF;
|
||||
break;
|
||||
case CSR_TIMEH:
|
||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||
sc_core::sc_time_stamp()
|
||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||
>> 32 & 0x00000000FFFFFFFF);
|
||||
break;
|
||||
[[likely]] default:
|
||||
ret_value = CSR[csr];
|
||||
break;
|
||||
}
|
||||
return ret_value;
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,11 +298,11 @@ namespace riscv_tlm {
|
|||
* @param value new value to register
|
||||
*/
|
||||
void setCSR(int csr, T value) {
|
||||
/* @FIXME: rv32mi-p-ma_fetch tests doesn't allow MISA to be writable,
|
||||
* but Volume II: Privileged Architecture v1.10 says MISA is writable (?)
|
||||
*/
|
||||
if (csr != CSR_MISA) {
|
||||
CSR[csr] = value;
|
||||
/* @FIXME: rv32mi-p-ma_fetch tests doesn't allow MISA to be writable,
|
||||
* but Volume II: Privileged Architecture v1.10 says MISA is writable (?)
|
||||
*/
|
||||
if (csr != CSR_MISA) {
|
||||
CSR[csr] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,83 +310,83 @@ namespace riscv_tlm {
|
|||
* Dump register data to console
|
||||
*/
|
||||
void dump() {
|
||||
std::cout << "************************************" << std::endl;
|
||||
std::cout << "Registers dump" << std::dec << std::endl;
|
||||
std::cout << std::setfill('0') << std::uppercase;
|
||||
std::cout << "x0 (zero): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[0];
|
||||
std::cout << " x1 (ra): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[1];
|
||||
std::cout << " x2 (sp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[2];
|
||||
std::cout << " x3 (gp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[3] << std::endl;
|
||||
std::cout << "************************************" << std::endl;
|
||||
std::cout << "Registers dump" << std::dec << std::endl;
|
||||
std::cout << std::setfill('0') << std::uppercase;
|
||||
std::cout << "x0 (zero): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[0];
|
||||
std::cout << " x1 (ra): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[1];
|
||||
std::cout << " x2 (sp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[2];
|
||||
std::cout << " x3 (gp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[3] << std::endl;
|
||||
|
||||
std::cout << "x4 (tp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[4];
|
||||
std::cout << " x5 (t0): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[5];
|
||||
std::cout << " x6 (t1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[6];
|
||||
std::cout << " x7 (t2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[7] << std::endl;
|
||||
std::cout << "x4 (tp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[4];
|
||||
std::cout << " x5 (t0): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[5];
|
||||
std::cout << " x6 (t1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[6];
|
||||
std::cout << " x7 (t2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[7] << std::endl;
|
||||
|
||||
std::cout << "x8 (s0/fp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[8];
|
||||
std::cout << " x9 (s1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[9];
|
||||
std::cout << " x10 (a0): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[10];
|
||||
std::cout << " x11 (a1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[11] << std::endl;
|
||||
std::cout << "x8 (s0/fp): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[8];
|
||||
std::cout << " x9 (s1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[9];
|
||||
std::cout << " x10 (a0): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[10];
|
||||
std::cout << " x11 (a1): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[11] << std::endl;
|
||||
|
||||
std::cout << "x12 (a2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[12];
|
||||
std::cout << " x13 (a3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[13];
|
||||
std::cout << " x14 (a4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[14];
|
||||
std::cout << " x15 (a5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[15] << std::endl;
|
||||
std::cout << "x12 (a2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[12];
|
||||
std::cout << " x13 (a3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[13];
|
||||
std::cout << " x14 (a4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[14];
|
||||
std::cout << " x15 (a5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[15] << std::endl;
|
||||
|
||||
std::cout << "x16 (a6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[16];
|
||||
std::cout << " x17 (a7): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[17];
|
||||
std::cout << " x18 (s2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[18];
|
||||
std::cout << " x19 (s3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[19] << std::endl;
|
||||
std::cout << "x16 (a6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[16];
|
||||
std::cout << " x17 (a7): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[17];
|
||||
std::cout << " x18 (s2): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[18];
|
||||
std::cout << " x19 (s3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[19] << std::endl;
|
||||
|
||||
std::cout << "x20 (s4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[20];
|
||||
std::cout << " x21 (s5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[21];
|
||||
std::cout << " x22 (s6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[22];
|
||||
std::cout << " x23 (s7): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[23] << std::endl;
|
||||
std::cout << "x20 (s4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[20];
|
||||
std::cout << " x21 (s5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[21];
|
||||
std::cout << " x22 (s6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[22];
|
||||
std::cout << " x23 (s7): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[23] << std::endl;
|
||||
|
||||
std::cout << "x24 (s8): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[24];
|
||||
std::cout << " x25 (s9): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[25];
|
||||
std::cout << " x26 (s10): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[26];
|
||||
std::cout << " x27 (s11): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[27] << std::endl;
|
||||
std::cout << "x24 (s8): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[24];
|
||||
std::cout << " x25 (s9): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[25];
|
||||
std::cout << " x26 (s10): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[26];
|
||||
std::cout << " x27 (s11): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[27] << std::endl;
|
||||
|
||||
std::cout << "x28 (t3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[28];
|
||||
std::cout << " x29 (t4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[29];
|
||||
std::cout << " x30 (t5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[30];
|
||||
std::cout << " x31 (t6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[31] << std::endl;
|
||||
std::cout << "x28 (t3): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[28];
|
||||
std::cout << " x29 (t4): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[29];
|
||||
std::cout << " x30 (t5): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[30];
|
||||
std::cout << " x31 (t6): 0x" << std::right << std::setw(8)
|
||||
<< std::hex << register_bank[31] << std::endl;
|
||||
|
||||
std::cout << "PC: 0x" << std::setw(8) << std::hex << register_PC << std::dec << std::endl;
|
||||
std::cout << "************************************" << std::endl;
|
||||
std::cout << "PC: 0x" << std::setw(8) << std::hex << register_PC << std::dec << std::endl;
|
||||
std::cout << "************************************" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -407,9 +408,9 @@ namespace riscv_tlm {
|
|||
Performance *perf;
|
||||
|
||||
void initCSR() {
|
||||
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
|
||||
| MISA_A_EXTENSION | MISA_I_BASE;
|
||||
CSR[CSR_MSTATUS] = MISA_MXL;
|
||||
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
|
||||
| MISA_A_EXTENSION | MISA_I_BASE;
|
||||
CSR[CSR_MSTATUS] = MISA_MXL;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,87 +34,88 @@ namespace riscv_tlm {
|
|||
public:
|
||||
extension_base(const T &instr, Registers<T> *register_bank,
|
||||
MemoryInterface *mem_interface) :
|
||||
m_instr(instr), regs(register_bank), mem_intf(mem_interface) {
|
||||
m_instr(instr), regs(register_bank), mem_intf(mem_interface) {
|
||||
|
||||
perf = Performance::getInstance();
|
||||
logger = spdlog::get("my_logger");
|
||||
perf = Performance::getInstance();
|
||||
logger = spdlog::get("my_logger");
|
||||
}
|
||||
|
||||
virtual ~extension_base() = default;
|
||||
|
||||
void setInstr(std::uint32_t p_instr) {
|
||||
m_instr = sc_dt::sc_uint<32>(p_instr);
|
||||
m_instr = sc_dt::sc_uint<32>(p_instr);
|
||||
}
|
||||
|
||||
void RaiseException(std::uint32_t cause, std::uint32_t inst) {
|
||||
std::uint32_t new_pc, current_pc, m_cause;
|
||||
std::uint32_t new_pc, current_pc, m_cause;
|
||||
|
||||
current_pc = regs->getPC();
|
||||
m_cause = regs->getCSR(CSR_MSTATUS);
|
||||
m_cause |= cause;
|
||||
current_pc = regs->getPC();
|
||||
m_cause = regs->getCSR(CSR_MSTATUS);
|
||||
m_cause |= cause;
|
||||
|
||||
new_pc = regs->getCSR(CSR_MTVEC);
|
||||
new_pc = regs->getCSR(CSR_MTVEC);
|
||||
|
||||
regs->setCSR(CSR_MEPC, current_pc);
|
||||
regs->setCSR(CSR_MEPC, current_pc);
|
||||
|
||||
if (cause == EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION) {
|
||||
regs->setCSR(CSR_MTVAL, inst);
|
||||
if (cause == EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION) {
|
||||
regs->setCSR(CSR_MTVAL, inst);
|
||||
} else {
|
||||
regs->setCSR(CSR_MTVAL, current_pc);
|
||||
regs->setCSR(CSR_MTVAL, current_pc);
|
||||
}
|
||||
|
||||
regs->setCSR(CSR_MCAUSE, cause);
|
||||
regs->setCSR(CSR_MSTATUS, m_cause);
|
||||
regs->setCSR(CSR_MCAUSE, cause);
|
||||
regs->setCSR(CSR_MSTATUS, m_cause);
|
||||
|
||||
regs->setPC(new_pc);
|
||||
regs->setPC(new_pc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. Exception! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
new_pc);
|
||||
logger->debug("{} ns. PC: 0x{:x}. Exception! new PC 0x{:x} ", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
new_pc);
|
||||
}
|
||||
|
||||
bool NOP() {
|
||||
logger->debug("{} ns. PC: 0x{:x}. NOP! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC());
|
||||
sc_core::sc_stop();
|
||||
return true;
|
||||
logger->debug("{} ns. PC: 0x{:x}. NOP! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC());
|
||||
sc_core::sc_stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* pure virtual functions */
|
||||
virtual std::int32_t opcode() const = 0;
|
||||
virtual T opcode() const = 0;
|
||||
|
||||
virtual std::int32_t get_rd() const {
|
||||
virtual unsigned int get_rd() const {
|
||||
return m_instr.range(11, 7);
|
||||
}
|
||||
|
||||
virtual void set_rd(std::int32_t value) {
|
||||
virtual void set_rd(unsigned int value) {
|
||||
m_instr.range(11, 7) = value;
|
||||
}
|
||||
|
||||
virtual std::int32_t get_rs1() const {
|
||||
virtual unsigned int get_rs1() const {
|
||||
return m_instr.range(19, 15);
|
||||
}
|
||||
|
||||
virtual void set_rs1(std::int32_t value) {
|
||||
virtual void set_rs1(unsigned int value) {
|
||||
m_instr.range(19, 15) = value;
|
||||
}
|
||||
|
||||
virtual std::int32_t get_rs2() const {
|
||||
virtual unsigned int get_rs2() const {
|
||||
return m_instr.range(24, 20);
|
||||
}
|
||||
|
||||
virtual void set_rs2(std::int32_t value) {
|
||||
virtual void set_rs2(unsigned int value) {
|
||||
m_instr.range(24, 20) = value;
|
||||
}
|
||||
|
||||
virtual std::int32_t get_funct3() const {
|
||||
virtual unsigned int get_funct3() const {
|
||||
return m_instr.range(14, 12);
|
||||
}
|
||||
|
||||
virtual void set_funct3(std::int32_t value) {
|
||||
virtual void set_funct3(unsigned int value) {
|
||||
m_instr.range(14, 12) = value;
|
||||
}
|
||||
|
||||
virtual void dump() const {
|
||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -6,440 +6,4 @@
|
|||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "A_extension.h"
|
||||
|
||||
namespace riscv_tlm {
|
||||
|
||||
op_A_Codes A_extension::decode() const {
|
||||
|
||||
switch (opcode()) {
|
||||
case A_LR:
|
||||
return OP_A_LR;
|
||||
break;
|
||||
case A_SC:
|
||||
return OP_A_SC;
|
||||
break;
|
||||
case A_AMOSWAP:
|
||||
return OP_A_AMOSWAP;
|
||||
break;
|
||||
case A_AMOADD:
|
||||
return OP_A_AMOADD;
|
||||
break;
|
||||
case A_AMOXOR:
|
||||
return OP_A_AMOXOR;
|
||||
break;
|
||||
case A_AMOAND:
|
||||
return OP_A_AMOAND;
|
||||
break;
|
||||
case A_AMOOR:
|
||||
return OP_A_AMOOR;
|
||||
break;
|
||||
case A_AMOMIN:
|
||||
return OP_A_AMOMIN;
|
||||
break;
|
||||
case A_AMOMAX:
|
||||
return OP_A_AMOMAX;
|
||||
break;
|
||||
case A_AMOMINU:
|
||||
return OP_A_AMOMINU;
|
||||
break;
|
||||
case A_AMOMAXU:
|
||||
return OP_A_AMOMAXU;
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
return OP_A_ERROR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return OP_A_ERROR;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_LR() {
|
||||
std::uint32_t mem_addr = 0;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
if (rs2 != 0) {
|
||||
std::cout << "ILEGAL INSTRUCTION, LR.W: rs2 != 0" << std::endl;
|
||||
RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, m_instr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
TLB_reserve(mem_addr);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.LR.W: x{:d}(0x{:x}) -> x{:d}(0x{:x}) ", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, mem_addr, rd, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_SC() {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = regs->getValue(rs2);
|
||||
|
||||
if (TLB_reserved(mem_addr)) {
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
regs->setValue(rd, 0); // SC writes 0 to rd on success
|
||||
} else {
|
||||
regs->setValue(rd, 1); // SC writes nonzero on failure
|
||||
}
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.SC.W: (0x{:x}) <- x{:d}(0x{:x}) ", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
mem_addr, rs2, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOSWAP() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// swap
|
||||
aux = regs->getValue(rs2);
|
||||
regs->setValue(rs2, static_cast<int32_t>(data));
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOSWAP");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOADD() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data + regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOADD");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOXOR() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data ^ regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOXOR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOAND() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data & regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOAND");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOOR() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// add
|
||||
data = data | regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOOR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOMIN() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// min
|
||||
aux = regs->getValue(rs2);
|
||||
if ((int32_t) data < (int32_t) aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMIN");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOMAX() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// >
|
||||
aux = regs->getValue(rs2);
|
||||
if ((int32_t) data > (int32_t) aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMAX");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOMINU() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// min
|
||||
aux = regs->getValue(rs2);
|
||||
if (data < aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMINU");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool A_extension::Exec_A_AMOMAXU() const {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t data;
|
||||
std::uint32_t aux;
|
||||
|
||||
/* These instructions must be atomic */
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
mem_addr = regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
|
||||
regs->setValue(rd, static_cast<int32_t>(data));
|
||||
|
||||
// max
|
||||
aux = regs->getValue(rs2);
|
||||
if (data > aux) {
|
||||
aux = data;
|
||||
}
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMAXU");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A_extension::TLB_reserve(std::uint32_t address) {
|
||||
TLB_A_Entries.insert(address);
|
||||
}
|
||||
|
||||
bool A_extension::TLB_reserved(std::uint32_t address) {
|
||||
if (TLB_A_Entries.count(address) == 1) {
|
||||
TLB_A_Entries.erase(address);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool A_extension::process_instruction(Instruction &inst) {
|
||||
bool PC_not_affected = true;
|
||||
|
||||
setInstr(inst.getInstr());
|
||||
|
||||
switch (decode()) {
|
||||
case OP_A_LR:
|
||||
Exec_A_LR();
|
||||
break;
|
||||
case OP_A_SC:
|
||||
Exec_A_SC();
|
||||
break;
|
||||
case OP_A_AMOSWAP:
|
||||
Exec_A_AMOSWAP();
|
||||
break;
|
||||
case OP_A_AMOADD:
|
||||
Exec_A_AMOADD();
|
||||
break;
|
||||
case OP_A_AMOXOR:
|
||||
Exec_A_AMOXOR();
|
||||
break;
|
||||
case OP_A_AMOAND:
|
||||
Exec_A_AMOAND();
|
||||
break;
|
||||
case OP_A_AMOOR:
|
||||
Exec_A_AMOOR();
|
||||
break;
|
||||
case OP_A_AMOMIN:
|
||||
Exec_A_AMOMIN();
|
||||
break;
|
||||
case OP_A_AMOMAX:
|
||||
Exec_A_AMOMAX();
|
||||
break;
|
||||
case OP_A_AMOMINU:
|
||||
Exec_A_AMOMINU();
|
||||
break;
|
||||
case OP_A_AMOMAXU:
|
||||
Exec_A_AMOMAXU();
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
std::cout << "A instruction not implemented yet" << std::endl;
|
||||
inst.dump();
|
||||
NOP();
|
||||
break;
|
||||
}
|
||||
|
||||
return PC_not_affected;
|
||||
}
|
||||
}
|
||||
#include "A_extension.h"
|
1475
src/BASE_ISA.cpp
1475
src/BASE_ISA.cpp
File diff suppressed because it is too large
Load Diff
12
src/CPU.cpp
12
src/CPU.cpp
|
@ -12,8 +12,8 @@ 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) {
|
||||
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();
|
||||
|
||||
|
@ -32,10 +32,10 @@ namespace riscv_tlm {
|
|||
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);
|
||||
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->reset();
|
||||
|
|
|
@ -5,749 +5,3 @@
|
|||
\date August 2018
|
||||
*/
|
||||
#include "C_extension.h"
|
||||
|
||||
namespace riscv_tlm {
|
||||
|
||||
op_C_Codes C_extension::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:
|
||||
return OP_C_FLW;
|
||||
break;
|
||||
case C_FSD:
|
||||
return OP_C_FSD;
|
||||
break;
|
||||
case C_SW:
|
||||
return OP_C_SW;
|
||||
break;
|
||||
case C_FSW:
|
||||
return OP_C_FSW;
|
||||
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:
|
||||
return OP_C_JAL;
|
||||
break;
|
||||
case C_LI:
|
||||
return OP_C_LI;
|
||||
break;
|
||||
case C_ADDI16SP:
|
||||
return OP_C_ADDI16SP;
|
||||
break;
|
||||
case C_SRLI:
|
||||
switch (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 (m_instr.range(6, 5)) {
|
||||
case C_3_SUB:
|
||||
return OP_C_SUB;
|
||||
break;
|
||||
case C_3_XOR:
|
||||
return OP_C_XOR;
|
||||
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:
|
||||
return OP_C_FLWSP;
|
||||
break;
|
||||
case C_JR:
|
||||
if (m_instr[12] == 0) {
|
||||
if (m_instr.range(6, 2) == 0) {
|
||||
return OP_C_JR;
|
||||
} else {
|
||||
return OP_C_MV;
|
||||
}
|
||||
} else {
|
||||
if (m_instr.range(11, 2) == 0) {
|
||||
return OP_C_EBREAK;
|
||||
} else if (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:
|
||||
[[unlikely]]
|
||||
default:
|
||||
return OP_C_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
[[unlikely]] default:
|
||||
|
||||
return OP_C_ERROR;
|
||||
break;
|
||||
}
|
||||
return OP_C_ERROR;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_JR() {
|
||||
std::uint32_t mem_addr;
|
||||
int rs1;
|
||||
int new_pc;
|
||||
|
||||
rs1 = get_rs1();
|
||||
mem_addr = 0;
|
||||
|
||||
new_pc = static_cast<std::int32_t>(
|
||||
static_cast<std::int32_t>((regs->getValue(rs1)) + static_cast<std::int32_t>(mem_addr)) & 0xFFFFFFFE);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.JR: PC <- 0x{:x}", sc_core::sc_time_stamp().value(), regs->getPC(), new_pc);
|
||||
|
||||
regs->setPC(new_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_MV() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = 0;
|
||||
rs2 = get_rs2();
|
||||
|
||||
calc = regs->getValue(rs1) + regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.MV: x{:d}(0x{:x}) + x{:d}(0x{:x}) -> x{:d}(0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, regs->getValue(rs1), rs2, regs->getValue(rs2), rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_ADD() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
calc = regs->getValue(rs1) + regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.ADD: x{:d} + x{} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_LWSP() {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1;
|
||||
std::int32_t imm;
|
||||
std::uint32_t data;
|
||||
|
||||
// lw rd, offset[7:2](x2)
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = 2;
|
||||
imm = get_imm_LWSP();
|
||||
|
||||
mem_addr = imm + regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
regs->setValue(rd, static_cast<std::int32_t>(data));
|
||||
|
||||
regs->setValue(rd, data);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.LWSP: x{:d} + {:d}(@0x{:x}) -> x{:d}({:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, imm, mem_addr, rd, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_ADDI4SPN() {
|
||||
int rd, rs1;
|
||||
std::int32_t imm;
|
||||
std::int32_t calc;
|
||||
|
||||
rd = get_rdp();
|
||||
rs1 = 2;
|
||||
imm = get_imm_ADDI4SPN();
|
||||
|
||||
if (imm == 0) {
|
||||
RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, m_instr);
|
||||
return false;
|
||||
}
|
||||
|
||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
||||
regs->setValue(rd, calc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI4SN: x{:d} + (0x{:x}) + {:d} -> x{:d}(0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, regs->getValue(rs1), imm, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_ADDI16SP() {
|
||||
// addi x2, x2, nzimm[9:4]
|
||||
int rd;
|
||||
std::int32_t imm;
|
||||
|
||||
if (get_rd() == 2) {
|
||||
int rs1;
|
||||
std::int32_t calc;
|
||||
|
||||
rd = 2;
|
||||
rs1 = 2;
|
||||
imm = get_imm_ADDI16SP();
|
||||
|
||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
||||
regs->setValue(rd, calc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI16SP: x{:d} + {:d} -> x{:d} (0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, imm, rd, calc);
|
||||
} else {
|
||||
/* C.LUI OPCODE */
|
||||
rd = get_rd();
|
||||
imm = get_imm_LUI();
|
||||
regs->setValue(rd, imm);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.LUI: x{:d} <- 0x{:x}", sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rd, imm);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SWSP() {
|
||||
// sw rs2, offset(x2)
|
||||
std::uint32_t mem_addr;
|
||||
int rs1, rs2;
|
||||
std::int32_t imm;
|
||||
std::uint32_t data;
|
||||
|
||||
rs1 = 2;
|
||||
rs2 = get_rs2();
|
||||
imm = get_imm_CSS();
|
||||
|
||||
mem_addr = imm + regs->getValue(rs1);
|
||||
data = regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SWSP: x{:d}(0x{:x}) -> x{:d} + {} (@0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs2, data, rs1, imm, mem_addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_BEQZ() {
|
||||
int rs1;
|
||||
int new_pc;
|
||||
std::uint32_t val1;
|
||||
|
||||
rs1 = get_rs1p();
|
||||
val1 = regs->getValue(rs1);
|
||||
|
||||
if (val1 == 0) {
|
||||
new_pc = static_cast<std::int32_t>(regs->getPC()) + get_imm_CB();
|
||||
regs->setPC(new_pc);
|
||||
} else {
|
||||
regs->incPCby2();
|
||||
new_pc = static_cast<std::int32_t>(regs->getPC());
|
||||
}
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.BEQZ: x{:d}(0x{:x}) == 0? -> PC (0xx{:d})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, val1, new_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_BNEZ() {
|
||||
int rs1;
|
||||
int new_pc;
|
||||
std::uint32_t val1;
|
||||
|
||||
rs1 = get_rs1p();
|
||||
val1 = regs->getValue(rs1);
|
||||
|
||||
if (val1 != 0) {
|
||||
new_pc = static_cast<std::int32_t>(regs->getPC()) + get_imm_CB();
|
||||
regs->setPC(new_pc);
|
||||
} else {
|
||||
regs->incPCby2(); //PC <- PC +2
|
||||
new_pc = static_cast<std::int32_t>(regs->getPC());
|
||||
}
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.BNEZ: x{:d}(0x{:x}) != 0? -> PC (0xx{:d})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, val1, new_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_LI() {
|
||||
int rd, rs1;
|
||||
std::int32_t imm;
|
||||
std::int32_t calc;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = 0;
|
||||
imm = get_imm_ADDI();
|
||||
|
||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
||||
regs->setValue(rd, calc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.LI: x{:d} ({:d}) + {:d} -> x{:d}(0x{:x}) ",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, regs->getValue(rs1), imm, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SRLI() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t shift;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2();
|
||||
|
||||
shift = rs2 & 0x1F;
|
||||
|
||||
calc = static_cast<std::uint32_t>(regs->getValue(rs1)) >> shift;
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SRLI: x{:d} >> {} -> x{:d}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, shift, rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SRAI() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t shift;
|
||||
std::int32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2();
|
||||
|
||||
shift = rs2 & 0x1F;
|
||||
|
||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) >> shift;
|
||||
regs->setValue(rd, calc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SRAI: x{:d} >> {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, shift, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SLLI() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t shift;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_imm_ADDI();
|
||||
|
||||
shift = rs2 & 0x1F;
|
||||
|
||||
calc = static_cast<std::uint32_t>(regs->getValue(rs1)) << shift;
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SLLI: x{:d} << {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, shift, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_ANDI() {
|
||||
int rd, rs1;
|
||||
std::uint32_t imm;
|
||||
std::uint32_t aux;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
imm = get_imm_ADDI();
|
||||
|
||||
aux = regs->getValue(rs1);
|
||||
calc = aux & imm;
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.ANDI: x{:d}(0x{:x}) AND 0x{:x} -> x{:d}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, aux, imm, rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SUB() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2p();
|
||||
|
||||
calc = regs->getValue(rs1) - regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SUB: x{:d} - x{:d} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_XOR() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2p();
|
||||
|
||||
calc = regs->getValue(rs1) ^ regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.XOR: x{:d} XOR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_OR() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2p();
|
||||
|
||||
calc = regs->getValue(rs1) | regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.OR: x{:d} OR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_AND() {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t calc;
|
||||
|
||||
rd = get_rs1p();
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2p();
|
||||
|
||||
calc = regs->getValue(rs1) & regs->getValue(rs2);
|
||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.AND: x{:d} AND x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_ADDI() const {
|
||||
int rd, rs1;
|
||||
std::int32_t imm;
|
||||
std::int32_t calc;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = rd;
|
||||
imm = get_imm_ADDI();
|
||||
|
||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
||||
regs->setValue(rd, calc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI: x{:d} + {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(), rs1, imm, rd, calc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_JALR() {
|
||||
std::uint32_t mem_addr = 0;
|
||||
int rd, rs1;
|
||||
int new_pc, old_pc;
|
||||
|
||||
rd = 1;
|
||||
rs1 = get_rs1();
|
||||
|
||||
old_pc = static_cast<std::int32_t>(regs->getPC());
|
||||
regs->setValue(rd, old_pc + 2);
|
||||
|
||||
new_pc = static_cast<std::int32_t>((regs->getValue(rs1) + mem_addr) & 0xFFFFFFFE);
|
||||
regs->setPC(new_pc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.JALR: x{:d} <- 0x{:x} PC <- 0xx{:x}", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rd, old_pc + 4, new_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_LW() {
|
||||
std::uint32_t mem_addr;
|
||||
int rd, rs1;
|
||||
std::int32_t imm;
|
||||
std::uint32_t data;
|
||||
|
||||
rd = get_rdp();
|
||||
rs1 = get_rs1p();
|
||||
imm = get_imm_L();
|
||||
|
||||
mem_addr = imm + regs->getValue(rs1);
|
||||
data = mem_intf->readDataMem(mem_addr, 4);
|
||||
perf->dataMemoryRead();
|
||||
regs->setValue(rd, static_cast<std::int32_t>(data));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.LW: x{:d}(0x{:x}) + {:d} (@0x{:x}) -> {:d} (0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs1, regs->getValue(rs1), imm, mem_addr, rd, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_SW() {
|
||||
std::uint32_t mem_addr;
|
||||
int rs1, rs2;
|
||||
std::int32_t imm;
|
||||
std::uint32_t data;
|
||||
|
||||
rs1 = get_rs1p();
|
||||
rs2 = get_rs2p();
|
||||
imm = get_imm_L();
|
||||
|
||||
mem_addr = imm + regs->getValue(rs1);
|
||||
data = regs->getValue(rs2);
|
||||
|
||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
||||
perf->dataMemoryWrite();
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.SW: x{:d}(0x{:x}) -> x{:d} + 0x{:x}(@0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rs2, data, rs1, imm, mem_addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_JAL(int m_rd) {
|
||||
std::int32_t mem_addr;
|
||||
int rd;
|
||||
int new_pc, old_pc;
|
||||
|
||||
rd = m_rd;
|
||||
mem_addr = get_imm_J();
|
||||
old_pc = static_cast<std::int32_t>(regs->getPC());
|
||||
|
||||
new_pc = old_pc + mem_addr;
|
||||
regs->setPC(new_pc);
|
||||
|
||||
old_pc = old_pc + 2;
|
||||
regs->setValue(rd, old_pc);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. C.JAL: x{:d} <- 0x{:x}. PC + 0x{:x} -> PC (0x{:x})",
|
||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
||||
rd, old_pc, mem_addr, new_pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::Exec_C_EBREAK() {
|
||||
|
||||
logger->debug("C.EBREAK");
|
||||
std::cout << "\n" << "C.EBRAK Instruction called, dumping information"
|
||||
<< "\n";
|
||||
regs->dump();
|
||||
std::cout << "Simulation time " << sc_core::sc_time_stamp() << "\n";
|
||||
perf->dump();
|
||||
|
||||
sc_core::sc_stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C_extension::process_instruction(Instruction &inst, bool *breakpoint) {
|
||||
bool PC_not_affected = true;
|
||||
|
||||
*breakpoint = false;
|
||||
|
||||
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_EBREAK:
|
||||
Exec_C_EBREAK();
|
||||
std::cout << "C_EBREAK" << std::endl;
|
||||
*breakpoint = true;
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
std::cout << "C instruction not implemented yet" << "\n";
|
||||
inst.dump();
|
||||
NOP();
|
||||
break;
|
||||
}
|
||||
|
||||
return PC_not_affected;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,292 +8,3 @@
|
|||
|
||||
#include "M_extension.h"
|
||||
|
||||
namespace riscv_tlm {
|
||||
|
||||
op_M_Codes M_extension::decode() const {
|
||||
|
||||
switch (opcode()) {
|
||||
case M_MUL:
|
||||
return OP_M_MUL;
|
||||
break;
|
||||
case M_MULH:
|
||||
return OP_M_MULH;
|
||||
break;
|
||||
case M_MULHSU:
|
||||
return OP_M_MULHSU;
|
||||
break;
|
||||
case M_MULHU:
|
||||
return OP_M_MULHU;
|
||||
break;
|
||||
case M_DIV:
|
||||
return OP_M_DIV;
|
||||
break;
|
||||
case M_DIVU:
|
||||
return OP_M_DIVU;
|
||||
break;
|
||||
case M_REM:
|
||||
return OP_M_REM;
|
||||
break;
|
||||
case M_REMU:
|
||||
return OP_M_REMU;
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
return OP_M_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return OP_M_ERROR;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_MUL() const {
|
||||
int rd, rs1, rs2;
|
||||
std::int32_t multiplier, multiplicand;
|
||||
std::int64_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
result = static_cast<std::int64_t>(multiplier * multiplicand);
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_MULH() const {
|
||||
int rd, rs1, rs2;
|
||||
std::int32_t multiplier, multiplicand;
|
||||
std::int64_t result;
|
||||
std::int32_t ret_value;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
|
||||
|
||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||
regs->setValue(rd, ret_value);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_MULHSU() const {
|
||||
int rd, rs1, rs2;
|
||||
std::int32_t multiplier;
|
||||
std::uint32_t multiplicand;
|
||||
std::int64_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
multiplicand = regs->getValue(rs2);
|
||||
|
||||
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
|
||||
result = (result >> 32) & 0x00000000FFFFFFFF;
|
||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_MULHU() const {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t multiplier, multiplicand;
|
||||
std::uint64_t result;
|
||||
std::int32_t ret_value;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
|
||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||
regs->setValue(rd, ret_value);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_DIV() const {
|
||||
int rd, rs1, rs2;
|
||||
std::int32_t divisor, dividend;
|
||||
std::int64_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = -1;
|
||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||
result = 0x0000000080000000;
|
||||
} else {
|
||||
result = dividend / divisor;
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
}
|
||||
|
||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_DIVU() const {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t divisor, dividend;
|
||||
std::uint64_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
dividend = regs->getValue(rs1);
|
||||
divisor = regs->getValue(rs2);
|
||||
|
||||
if (divisor == 0) {
|
||||
result = -1;
|
||||
} else {
|
||||
result = dividend / divisor;
|
||||
result = result & 0x00000000FFFFFFFF;
|
||||
}
|
||||
|
||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_REM() const {
|
||||
int rd, rs1, rs2;
|
||||
std::int32_t divisor, dividend;
|
||||
std::int32_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = dividend;
|
||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = dividend % divisor;
|
||||
}
|
||||
|
||||
regs->setValue(rd, result);
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::Exec_M_REMU() const {
|
||||
int rd, rs1, rs2;
|
||||
std::uint32_t divisor, dividend;
|
||||
std::uint32_t result;
|
||||
|
||||
rd = get_rd();
|
||||
rs1 = get_rs1();
|
||||
rs2 = get_rs2();
|
||||
|
||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
||||
|
||||
if (divisor == 0) {
|
||||
result = dividend;
|
||||
} else {
|
||||
result = dividend % divisor;
|
||||
}
|
||||
|
||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||
|
||||
logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
||||
regs->getPC(),
|
||||
rs1, rs2, rd, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool M_extension::process_instruction(Instruction &inst) {
|
||||
bool PC_not_affected = true;
|
||||
|
||||
setInstr(inst.getInstr());
|
||||
|
||||
switch (decode()) {
|
||||
case OP_M_MUL:
|
||||
Exec_M_MUL();
|
||||
break;
|
||||
case OP_M_MULH:
|
||||
Exec_M_MULH();
|
||||
break;
|
||||
case OP_M_MULHSU:
|
||||
Exec_M_MULHSU();
|
||||
break;
|
||||
case OP_M_MULHU:
|
||||
Exec_M_MULHU();
|
||||
break;
|
||||
case OP_M_DIV:
|
||||
Exec_M_DIV();
|
||||
break;
|
||||
case OP_M_DIVU:
|
||||
Exec_M_DIVU();
|
||||
break;
|
||||
case OP_M_REM:
|
||||
Exec_M_REM();
|
||||
break;
|
||||
case OP_M_REMU:
|
||||
Exec_M_REMU();
|
||||
break;
|
||||
[[unlikely]] default:
|
||||
std::cout << "M instruction not implemented yet" << "\n";
|
||||
inst.dump();
|
||||
//NOP(inst);
|
||||
sc_core::sc_stop();
|
||||
break;
|
||||
}
|
||||
|
||||
return PC_not_affected;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue