417 lines
7.4 KiB
C++
417 lines
7.4 KiB
C++
|
/*!
|
||
|
\file A_extension.h
|
||
|
\brief Implement A extensions part of the RISC-V
|
||
|
\author Màrius Montón
|
||
|
\date December 2018
|
||
|
*/
|
||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
|
||
|
#include "A_extension.h"
|
||
|
|
||
|
op_A_Codes A_extension::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;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_LR() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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);
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
TLB_reserve(mem_addr);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "LR.W: x" << rs1 << " (@0x"
|
||
|
<< std::hex << mem_addr << std::dec << ") -> x" << rd << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_SC() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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) == true) {
|
||
|
mem_intf->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) << std::dec << "SC.W: (@0x" << std::hex << mem_addr
|
||
|
<< std::dec << ") <- x" << rs2 << std::hex << "(0x" << data << ")"
|
||
|
<< std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_AMOSWAP() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
uint32_t data;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// swap
|
||
|
aux = regs->getValue(rs2);
|
||
|
regs->setValue(rs2, data);
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOSWAP " << std::endl;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_AMOADD() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// add
|
||
|
data = data + regs->getValue(rs2);
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, data, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOADD " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_AMOXOR() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// add
|
||
|
data = data ^ regs->getValue(rs2);
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, data, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOXOR " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
bool A_extension::Exec_A_AMOAND() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// add
|
||
|
data = data & regs->getValue(rs2);
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, data, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOAND " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool A_extension::Exec_A_AMOOR() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// add
|
||
|
data = data | regs->getValue(rs2);
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, data, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOOR " << std::endl;
|
||
|
return true;
|
||
|
}
|
||
|
bool A_extension::Exec_A_AMOMIN() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
uint32_t data;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// min
|
||
|
aux = regs->getValue(rs2);
|
||
|
if ((int32_t) data < (int32_t) aux) {
|
||
|
aux = data;
|
||
|
}
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOMIN " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
bool A_extension::Exec_A_AMOMAX() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
uint32_t data;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// >
|
||
|
aux = regs->getValue(rs2);
|
||
|
if ((int32_t) data > (int32_t) aux) {
|
||
|
aux = data;
|
||
|
}
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOMAX " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
bool A_extension::Exec_A_AMOMINU() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
uint32_t data;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// min
|
||
|
aux = regs->getValue(rs2);
|
||
|
if (data < aux) {
|
||
|
aux = data;
|
||
|
}
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOMINU " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
bool A_extension::Exec_A_AMOMAXU() {
|
||
|
uint32_t mem_addr = 0;
|
||
|
int rd, rs1, rs2;
|
||
|
uint32_t data;
|
||
|
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);
|
||
|
|
||
|
regs->setValue(rd, data);
|
||
|
|
||
|
// max
|
||
|
aux = regs->getValue(rs2);
|
||
|
if (data > aux) {
|
||
|
aux = data;
|
||
|
}
|
||
|
|
||
|
mem_intf->writeDataMem(mem_addr, aux, 4);
|
||
|
|
||
|
log->SC_log(Log::INFO) << std::dec << "AMOMAXU " << std::endl;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void A_extension::TLB_reserve(uint32_t address) {
|
||
|
TLB_A_Entries.insert(address);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool A_extension::TLB_reserved(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;
|
||
|
default:
|
||
|
std::cout << "A instruction not implemented yet" << std::endl;
|
||
|
inst.dump();
|
||
|
NOP();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return PC_not_affected;
|
||
|
}
|