diff --git a/inc/A_Instruction.h b/inc/A_Instruction.h new file mode 100644 index 0000000..b589f5e --- /dev/null +++ b/inc/A_Instruction.h @@ -0,0 +1,128 @@ +/*! + \file A_Instruction.h + \brief Decode A extensions part of the RISC-V + \author Màrius Montón + \date December 2018 +*/ + +#ifndef A_INSTRUCTION__H +#define A_INSTRUCTION__H + +#include "systemc" + +using namespace sc_core; +using namespace sc_dt; +using namespace std; + +typedef enum { + OP_A_LR, + OP_A_SC, + OP_A_AMOSWAP, + OP_A_AMOADD, + OP_A_AMOXOR, + OP_A_AMOAND, + OP_A_AMOOR, + OP_A_AMOMIN, + OP_A_AMOMAX, + OP_A_AMOMINU, + OP_A_AMOMAXU, + + OP_A_ERROR +} op_A_Codes; + + +typedef enum { + A_LR = 0b00010, + A_SC = 0b00011, + A_AMOSWAP = 0b00001, + A_AMOADD = 0b00000, + A_AMOXOR = 0b00100, + A_AMOAND = 0b01100, + A_AMOOR = 0b01000, + A_AMOMIN = 0b10000, + A_AMOMAX = 0b10100, + A_AMOMINU = 0b11000, + A_AMOMAXU = 0b11100, +} A_Codes; + +/** + * @brief Instruction decoding and fields access + */ +class A_Instruction{ +public: + + /** + * @brief Constructor + * @param instr Instruction to decode + */ + A_Instruction(sc_uint<32> instr); + + /** + * @brief Access to opcode field + * @return return opcode field + */ + inline int32_t opcode() { + return a_instr.range(31,27); + } + + /** + * @brief Access to rd field + * @return rd field + */ + inline int32_t get_rd() { + return a_instr.range(11, 7); + } + + inline void set_rd(int32_t value) { + a_instr.range(11,7) = value; + } + + + /** + * @brief Access to rs1 field + * @return rs1 field + */ + inline int32_t get_rs1() { + return a_instr.range(19, 15); + } + + inline void set_rs1(int32_t value) { + a_instr.range(19,15) = value; + } + + + /** + * @brief Access to rs2 field + * @return rs2 field + */ + inline int32_t get_rs2() { + return a_instr.range(24, 20); + } + + inline void set_rs2(int32_t value) { + a_instr.range(24,20) = value; + } + + + inline int32_t get_funct3() { + return a_instr.range(14, 12); + } + + inline void set_funct3(int32_t value) { + a_instr.range(14,12) = value; + } + + /** + * @brief Decodes opcode of instruction + * @return opcode of instruction + */ + op_A_Codes decode(); + + inline void dump() { + cout << hex << "0x" << a_instr << dec << endl; + } +private: + sc_uint<32> a_instr; +}; + +#endif diff --git a/inc/CPU.h b/inc/CPU.h index 55abf53..e367555 100644 --- a/inc/CPU.h +++ b/inc/CPU.h @@ -21,6 +21,7 @@ #include "Instruction.h" #include "C_Instruction.h" #include "M_Instruction.h" +#include "A_Instruction.h" using namespace sc_core; using namespace sc_dt; @@ -60,6 +61,8 @@ private: bool process_m_instruction(Instruction &inst); + bool process_a_instruction(Instruction inst); + void CPU_thread(void); }; diff --git a/inc/Execute.h b/inc/Execute.h index d9f322e..5ac5334 100644 --- a/inc/Execute.h +++ b/inc/Execute.h @@ -10,7 +10,7 @@ #define SC_INCLUDE_DYNAMIC_PROCESSES #include "systemc" - +#include #include "tlm.h" #include "tlm_utils/simple_initiator_socket.h" @@ -18,6 +18,7 @@ #include "Instruction.h" #include "C_Instruction.h" #include "M_Instruction.h" +#include "A_Instruction.h" #include "Registers.h" #include "Log.h" @@ -102,8 +103,10 @@ public: /*********************** Privileged Instructions ******************************/ bool MRET(Instruction &inst); + bool SRET(Instruction &inst); bool WFI(Instruction &inst); - + bool SFENCE(Instruction &inst); + /* C Extensions */ bool C_JR(Instruction &inst); bool C_MV(Instruction &inst); @@ -134,6 +137,19 @@ public: bool M_REM(Instruction &inst); bool M_REMU(Instruction &inst); + /* A Extensinos */ + bool A_LR(Instruction &inst); + bool A_SC(Instruction &inst); + bool A_AMOSWAP(Instruction &inst); + bool A_AMOADD(Instruction &inst); + bool A_AMOXOR(Instruction &inst); + bool A_AMOAND(Instruction &inst); + bool A_AMOOR(Instruction &inst); + bool A_AMOMIN(Instruction &inst); + bool A_AMOMAX(Instruction &inst); + bool A_AMOMINU(Instruction &inst); + bool A_AMOMAXU(Instruction &inst); + bool NOP(Instruction &inst); private: @@ -142,6 +158,11 @@ private: void RaiseException(uint32_t cause, uint32_t inst = 0); + std::set TLB_A_Entries; + + void TLB_reserve(uint32_t address); + bool TLB_reserved(uint32_t address); + Registers *regs; Performance *perf; Log *log; diff --git a/inc/Instruction.h b/inc/Instruction.h index a9214b0..99efa4a 100644 --- a/inc/Instruction.h +++ b/inc/Instruction.h @@ -90,6 +90,7 @@ OP_URET, OP_SRET, OP_MRET, OP_WFI, +OP_SFENCE, OP_ERROR } opCodes; @@ -158,6 +159,8 @@ typedef enum { SRET_F = 0b000100000010, MRET_F = 0b001100000010, WFI_F = 0b000100000101, + SFENCE_F = 0b0001001, + ECALL_F3= 0b000, CSRRW = 0b001, CSRRS = 0b010, diff --git a/src/A_Instruction.cpp b/src/A_Instruction.cpp new file mode 100644 index 0000000..7293534 --- /dev/null +++ b/src/A_Instruction.cpp @@ -0,0 +1,51 @@ +#include "A_Instruction.h" + + +A_Instruction::A_Instruction(sc_uint<32> instr) { + a_instr = instr; +} + +op_A_Codes A_Instruction::decode() { + + 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; + default: + return OP_A_ERROR; + break; + + } + + return OP_A_ERROR; +} diff --git a/src/CPU.cpp b/src/CPU.cpp index fe10425..08fd317 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -157,6 +157,56 @@ bool CPU::process_m_instruction(Instruction &inst) { return PC_not_affected; } + +bool CPU::process_a_instruction(Instruction inst) { + bool PC_not_affected = true; + + A_Instruction a_inst(inst.getInstr()); + + switch(a_inst.decode()) { + case OP_A_LR: + exec->A_LR(inst); + break; + case OP_A_SC: + exec->A_SC(inst); + break; + case OP_A_AMOSWAP: + exec->A_AMOSWAP(inst); + break; + case OP_A_AMOADD: + exec->A_AMOADD(inst); + break; + case OP_A_AMOXOR: + exec->A_AMOXOR(inst); + break; + case OP_A_AMOAND: + exec->A_AMOAND(inst); + break; + case OP_A_AMOOR: + exec->A_AMOOR(inst); + break; + case OP_A_AMOMIN: + exec->A_AMOMIN(inst); + break; + case OP_A_AMOMAX: + exec->A_AMOMAX(inst); + break; + case OP_A_AMOMINU: + exec->A_AMOMINU(inst); + break; + case OP_A_AMOMAXU: + exec->A_AMOMAXU(inst); + break; + default: + std::cout << "A instruction not implemented yet" << endl; + inst.dump(); + exec->NOP(inst); + break; + } + + return PC_not_affected; +} + bool CPU::process_base_instruction(Instruction &inst) { bool PC_not_affected = true; @@ -280,17 +330,6 @@ bool CPU::process_base_instruction(Instruction &inst) { case OP_AND: exec->AND(inst); break; -#if 0 - case OP_CSRRW: - exec->CSRRW(inst); - break; - case OP_CSRRS: - exec->CSRRS(inst); - break; - case OP_CSRRC: - exec->CSRRC(inst); - break; -#endif case OP_FENCE: exec->FENCE(inst); break; @@ -323,9 +362,16 @@ bool CPU::process_base_instruction(Instruction &inst) { exec->MRET(inst); PC_not_affected = false; break; + case OP_SRET: + exec->SRET(inst); + PC_not_affected = false; + break; case OP_WFI: exec->WFI(inst); break; + case OP_SFENCE: + exec->SFENCE(inst); + break; default: std::cout << "Wrong instruction" << endl; inst.dump(); @@ -361,6 +407,7 @@ void CPU::CPU_thread(void) { while(1) { /* Get new PC value */ + //cout << "CPU: PC 0x" << hex << (uint32_t) register_bank->getPC() << endl; trans->set_address( register_bank->getPC() ); instr_bus->b_transport( *trans, delay); @@ -388,6 +435,10 @@ void CPU::CPU_thread(void) { PC_not_affected = process_m_instruction(inst); incPCby2 = false; break; + case A_EXTENSION: + PC_not_affected = process_a_instruction(inst); + incPCby2 = false; + break; default: std::cout << "Extension not implemented yet" << std::endl; inst.dump(); diff --git a/src/Execute.cpp b/src/Execute.cpp index b4b2a8b..ed948d2 100644 --- a/src/Execute.cpp +++ b/src/Execute.cpp @@ -322,8 +322,9 @@ bool Execute::LW(Instruction &inst, bool c_extension) { regs->setValue(rd, data); log->SC_log(Log::INFO) << dec << "C.LW: x" - << rs1 << " + " << imm << " (@0x" << hex - << mem_addr << dec << ") -> x" << rd << endl; + << rs1 << "(0x" << hex << regs->getValue(rs1) << ") + " + << dec << imm << " (@0x" << hex << mem_addr << dec << ") -> x" << rd << hex + << " (0x" << data << ")"<< endl; return true; } @@ -565,20 +566,22 @@ bool Execute::ORI(Instruction &inst) { bool Execute::ANDI(Instruction &inst) { int rd, rs1; - int32_t imm; + uint32_t imm; uint32_t calc; + uint32_t aux; rd = inst.get_rd(); rs1 = inst.get_rs1(); imm = inst.get_imm_I(); - calc = regs->getValue(rs1) & imm; + aux = regs->getValue(rs1); + calc = aux & imm; regs->setValue(rd, calc); log->SC_log(Log::INFO) << "ANDI: x" - << rs1 << " AND " + << rs1 << "(0x" << hex << aux << ") AND 0x" << imm << " -> x" - << rd << endl; + << dec << rd << "(0x" << hex << calc << ")" << endl; return true; } @@ -662,12 +665,16 @@ bool Execute::ADD(Instruction &inst) { rs2 = inst.get_rs2(); calc = regs->getValue(rs1) + regs->getValue(rs2); + + // log->SC_log(Log::INFO) << "ADD 0x" << hex << regs->getValue(rs1) + // << " + 0x" << regs->getValue(rs2) << " = " << calc << endl; + regs->setValue(rd, calc); - log->SC_log(Log::INFO) << "ADD: x" + log->SC_log(Log::INFO) << "ADD: x" << dec << rs1 << " + x" << rs2 << " -> x" - << rd << endl; + << rd << hex << "(0x" << calc << ")"<< endl; return true; } @@ -922,6 +929,7 @@ bool Execute::CSRRS(Instruction &inst) { csr = inst.get_csr(); if (rd == 0) { + log->SC_log(Log::INFO) << "CSRRS with rd1 == 0, doing nothing." << endl; return false; } @@ -952,6 +960,7 @@ bool Execute::CSRRC(Instruction &inst) { csr = inst.get_csr(); if (rd == 0) { + log->SC_log(Log::INFO) << "CSRRC with rd1 == 0, doing nothing." << endl; return true; } @@ -1048,7 +1057,8 @@ bool Execute::CSRRCI(Instruction &inst) { log->SC_log(Log::INFO) << "CSRRCI: CSR #" << csr << " -> x" << rd - << ". x" << rs1 << " & CSR #" << csr << endl; + << ". x" << rs1 << " & CSR #" << csr + << "(0x" << hex << aux << ")"<< endl; return true; } @@ -1066,6 +1076,16 @@ bool Execute::MRET(Instruction &inst) { return true; } +bool Execute::SRET(Instruction &inst) { + uint32_t new_pc = 0; + + new_pc = regs->getCSR(CSR_SEPC); + regs->setPC(new_pc); + + log->SC_log(Log::INFO) << "SRET: PC <- 0x" << hex << new_pc << endl; + + return true; +} bool Execute::WFI(Instruction &inst) { log->SC_log(Log::INFO) << "WFI" << endl; @@ -1073,6 +1093,12 @@ bool Execute::WFI(Instruction &inst) { return true; } +bool Execute::SFENCE(Instruction &inst) { + log->SC_log(Log::INFO) << "SFENCE" << endl; + + return true; +} + /**************************** C Instructions **********************************/ bool Execute::C_JR(Instruction &inst) { @@ -1390,7 +1416,8 @@ bool Execute::C_SLLI(Instruction &inst) { bool Execute::C_ANDI(Instruction &inst) { int rd, rs1; - int32_t imm; + uint32_t imm; + uint32_t aux; uint32_t calc; C_Instruction c_inst(inst.getInstr()); @@ -1399,11 +1426,12 @@ bool Execute::C_ANDI(Instruction &inst) { rs1 = c_inst.get_rs1p(); imm = c_inst.get_imm_ADDI(); - calc = regs->getValue(rs1) & imm; + aux = regs->getValue(rs1); + calc = aux & imm; regs->setValue(rd, calc); log->SC_log(Log::INFO) << "C.ANDI: x" - << rs1 << " AND " + << rs1 << "(" << aux << ") AND " << imm << " -> x" << rd << endl; @@ -1719,6 +1747,329 @@ bool Execute::M_REMU(Instruction &inst) { return true; } + +bool Execute::A_LR(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + if (rs2 != 0) { + cout << "ILEGAL INSTRUCTION, LR.W: rs2 != 0" << endl; + RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION); + + return false; + } + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + regs->setValue(rd, data); + + TLB_reserve(mem_addr); + + log->SC_log(Log::INFO) << dec << "LR.W: x" + << rs1 << " (@0x" << hex << mem_addr + << dec << ") -> x" << rd << endl; + + return true; +} + +bool Execute::A_SC(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = regs->getValue(rs2); + + if (TLB_reserved(mem_addr) == true) { + writeDataMem(mem_addr, data, 4); + regs->setValue(rd, 0); // SC writes 0 to rd on success + } else { + regs->setValue(rd, 1); // SC writes nonzero on failure + } + + log->SC_log(Log::INFO) << dec << "SC.W: (@0x" << + hex << mem_addr << dec << ") <- x" << rs2 << + hex << "(0x" << data << ")" << endl; + + return true; +} + +bool Execute::A_AMOSWAP(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + uint32_t aux; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // swap + aux = regs->getValue(rs2); + regs->setValue(rs2, data); + + writeDataMem(mem_addr, aux, 4); + + log->SC_log(Log::INFO) << dec << "AMOSWAP " << endl; + return true; +} + +bool Execute::A_AMOADD(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // add + data = data + regs->getValue(rs2); + + writeDataMem(mem_addr, data, 4); + + log->SC_log(Log::INFO) << dec << "AMOADD " << endl; + + return true; +} + +bool Execute::A_AMOXOR(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // add + data = data ^ regs->getValue(rs2); + + writeDataMem(mem_addr, data, 4); + + log->SC_log(Log::INFO) << dec << "AMOXOR " << endl; + + return true; +} +bool Execute::A_AMOAND(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // add + data = data & regs->getValue(rs2); + + writeDataMem(mem_addr, data, 4); + + log->SC_log(Log::INFO) << dec << "AMOAND " << endl; + + return true; +} +bool Execute::A_AMOOR(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // add + data = data | regs->getValue(rs2); + + writeDataMem(mem_addr, data, 4); + + log->SC_log(Log::INFO) << dec << "AMOOR " << endl; + return true; +} +bool Execute::A_AMOMIN(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + uint32_t aux; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // min + aux = regs->getValue(rs2); + if ((int32_t)data < (int32_t)aux) { + aux = data; + } + + writeDataMem(mem_addr, aux, 4); + + log->SC_log(Log::INFO) << dec << "AMOMIN " << endl; + + return true; +} +bool Execute::A_AMOMAX(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + uint32_t aux; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // > + aux = regs->getValue(rs2); + if ((int32_t)data > (int32_t)aux) { + aux = data; + } + + writeDataMem(mem_addr, aux, 4); + + log->SC_log(Log::INFO) << dec << "AMOMAX " << endl; + + return true; +} +bool Execute::A_AMOMINU(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + uint32_t aux; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // min + aux = regs->getValue(rs2); + if (data < aux) { + aux = data; + } + + writeDataMem(mem_addr, aux, 4); + + log->SC_log(Log::INFO) << dec << "AMOMINU " << endl; + + return true; +} +bool Execute::A_AMOMAXU(Instruction &inst) { + uint32_t mem_addr = 0; + int rd, rs1, rs2; + uint32_t data; + uint32_t aux; + + A_Instruction a_inst(inst.getInstr()); + + /* These instructions must be atomic */ + + rd = a_inst.get_rd(); + rs1 = a_inst.get_rs1(); + rs2 = a_inst.get_rs2(); + + mem_addr = regs->getValue(rs1); + data = readDataMem(mem_addr, 4); + + regs->setValue(rd, data); + + // max + aux = regs->getValue(rs2); + if (data > aux) { + aux = data; + } + + writeDataMem(mem_addr, aux, 4); + + log->SC_log(Log::INFO) << dec << "AMOMAXU " << endl; + + return true; +} + + bool Execute::NOP(Instruction &inst) { cout << endl; regs->dump(); @@ -1805,3 +2156,18 @@ void Execute::RaiseException(uint32_t cause, uint32_t inst) { log->SC_log(Log::INFO) << "Exception! new PC " << hex << new_pc << endl; } + + +void Execute::TLB_reserve(uint32_t address) { + TLB_A_Entries.insert(address); + return; +} + +bool Execute::TLB_reserved(uint32_t address) { + if (TLB_A_Entries.count(address) == 1) { + TLB_A_Entries.erase(address); + return true; + } else { + return false; + } +} diff --git a/src/Instruction.cpp b/src/Instruction.cpp index 2e0aca9..c5faecc 100644 --- a/src/Instruction.cpp +++ b/src/Instruction.cpp @@ -130,6 +130,11 @@ opCodes Instruction::decode() { return OP_MRET; case WFI_F: return OP_WFI; + case SFENCE_F: + return OP_SFENCE; + } + if (m_instr.range(31,25) == 0b0001001) { + return OP_SFENCE; } break; case CSRRW: @@ -162,6 +167,8 @@ extension_t Instruction::check_extension() { if ( (m_instr.range(6,0) == 0b0110011) && (m_instr.range(31,25) == 0b0000001) ){ return M_EXTENSION; + } else if (m_instr.range(6,0) == 0b0101111) { + return A_EXTENSION; } else if (m_instr.range(1,0) == 0b11) { return BASE_EXTENSION; } else if (m_instr.range(1,0) == 0b00) { @@ -171,7 +178,6 @@ extension_t Instruction::check_extension() { } else if (m_instr.range(1,0) == 0b10) { return C_EXTENSION; } else if (m_instr.range(6,0) == 0b0101111) { - cout << "check_extension A not yet implemented" << endl; return A_EXTENSION; } else { return UNKNOWN_EXTENSION;