Files
vortex/sim/simx/decode.cpp
Blaise Tine c1e168fdbe Vortex 2.0 changes:
+ Microarchitecture optimizations
+ 64-bit support
+ Xilinx FPGA support
+ LLVM-16 support
+ Refactoring and quality control fixes

minor update

minor update

minor update

minor update

minor update

minor update

cleanup

cleanup

cache bindings and memory perf refactory

minor update

minor update

hw unit tests fixes

minor update

minor update

minor update

minor update

minor update

minor udpate

minor update

minor update

minor update

minor update

minor update

minor update

minor update

minor updates

minor updates

minor update

minor update

minor update

minor update

minor update

minor update

minor updates

minor updates

minor updates

minor updates

minor update

minor update
2023-11-10 02:47:05 -08:00

746 lines
20 KiB
C++

// Copyright © 2019-2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <vector>
#include <unordered_map>
#include <util.h>
#include "debug.h"
#include "types.h"
#include "decode.h"
#include "arch.h"
#include "instr.h"
using namespace vortex;
static const std::unordered_map<Opcode, InstType> sc_instTable = {
{Opcode::R_INST, InstType::R_TYPE},
{Opcode::L_INST, InstType::I_TYPE},
{Opcode::I_INST, InstType::I_TYPE},
{Opcode::S_INST, InstType::S_TYPE},
{Opcode::B_INST, InstType::B_TYPE},
{Opcode::LUI_INST, InstType::U_TYPE},
{Opcode::AUIPC_INST, InstType::U_TYPE},
{Opcode::JAL_INST, InstType::J_TYPE},
{Opcode::JALR_INST, InstType::I_TYPE},
{Opcode::SYS_INST, InstType::I_TYPE},
{Opcode::FENCE, InstType::I_TYPE},
{Opcode::AMO, InstType::R_TYPE},
{Opcode::FL, InstType::I_TYPE},
{Opcode::FS, InstType::S_TYPE},
{Opcode::FCI, InstType::R_TYPE},
{Opcode::FMADD, InstType::R4_TYPE},
{Opcode::FMSUB, InstType::R4_TYPE},
{Opcode::FMNMADD, InstType::R4_TYPE},
{Opcode::FMNMSUB, InstType::R4_TYPE},
{Opcode::VSET, InstType::V_TYPE},
{Opcode::EXT1, InstType::R_TYPE},
{Opcode::EXT2, InstType::R4_TYPE},
{Opcode::R_INST_W, InstType::R_TYPE},
{Opcode::I_INST_W, InstType::I_TYPE},
};
enum Constants {
width_opcode= 7,
width_reg = 5,
width_func2 = 2,
width_func3 = 3,
width_func6 = 6,
width_func7 = 7,
width_mop = 3,
width_vmask = 1,
width_i_imm = 12,
width_j_imm = 20,
width_v_imm = 11,
width_aq = 1,
width_rl = 1,
shift_opcode= 0,
shift_rd = width_opcode,
shift_func3 = shift_rd + width_reg,
shift_rs1 = shift_func3 + width_func3,
shift_rs2 = shift_rs1 + width_reg,
shift_func2 = shift_rs2 + width_reg,
shift_func7 = shift_rs2 + width_reg,
shift_rs3 = shift_func7 + width_func2,
shift_vmop = shift_func7 + width_vmask,
shift_vnf = shift_vmop + width_mop,
shift_func6 = shift_func7 + width_vmask,
shift_vset = shift_func7 + width_func6,
mask_opcode = (1 << width_opcode) - 1,
mask_reg = (1 << width_reg) - 1,
mask_func2 = (1 << width_func2) - 1,
mask_func3 = (1 << width_func3) - 1,
mask_func6 = (1 << width_func6) - 1,
mask_func7 = (1 << width_func7) - 1,
mask_i_imm = (1 << width_i_imm) - 1,
mask_j_imm = (1 << width_j_imm) - 1,
mask_v_imm = (1 << width_v_imm) - 1,
};
static const char* op_string(const Instr &instr) {
auto opcode = instr.getOpcode();
auto func2 = instr.getFunc2();
auto func3 = instr.getFunc3();
auto func7 = instr.getFunc7();
auto rs2 = instr.getRSrc(1);
auto imm = instr.getImm();
switch (opcode) {
case Opcode::LUI_INST: return "LUI";
case Opcode::AUIPC_INST: return "AUIPC";
case Opcode::R_INST:
if (func7 & 0x1) {
switch (func3) {
case 0: return "MUL";
case 1: return "MULH";
case 2: return "MULHSU";
case 3: return "MULHU";
case 4: return "DIV";
case 5: return "DIVU";
case 6: return "REM";
case 7: return "REMU";
default:
std::abort();
}
} else {
switch (func3) {
case 0: return func7 ? "SUB" : "ADD";
case 1: return "SLL";
case 2: return "SLT";
case 3: return "SLTU";
case 4: return "XOR";
case 5: return (func7 & 0x20) ? "SRA" : "SRL";
case 6: return "OR";
case 7: return "AND";
default:
std::abort();
}
}
case Opcode::I_INST:
switch (func3) {
case 0: return "ADDI";
case 1: return "SLLI";
case 2: return "SLTI";
case 3: return "SLTIU";
case 4: return "XORI";
case 5: return (func7 & 0x20) ? "SRAI" : "SRLI";
case 6: return "ORI";
case 7: return "ANDI";
default:
std::abort();
}
case Opcode::B_INST:
switch (func3) {
case 0: return "BEQ";
case 1: return "BNE";
case 4: return "BLT";
case 5: return "BGE";
case 6: return "BLTU";
case 7: return "BGEU";
default:
std::abort();
}
case Opcode::JAL_INST: return "JAL";
case Opcode::JALR_INST: return "JALR";
case Opcode::L_INST:
switch (func3) {
case 0: return "LB";
case 1: return "LH";
case 2: return "LW";
case 3: return "LD";
case 4: return "LBU";
case 5: return "LHU";
case 6: return "LWU";
default:
std::abort();
}
case Opcode::S_INST:
switch (func3) {
case 0: return "SB";
case 1: return "SH";
case 2: return "SW";
case 3: return "SD";
default:
std::abort();
}
case Opcode::R_INST_W:
if (func7 & 0x1){
switch (func3) {
case 0: return "MULW";
case 4: return "DIVW";
case 5: return "DIVUW";
case 6: return "REMW";
case 7: return "REMUW";
default:
std::abort();
}
} else {
switch (func3) {
case 0: return func7 ? "SUBW" : "ADDW";
case 1: return "SLLW";
case 5: return func7 ? "SRAW" : "SRLW";
default:
std::abort();
}
}
case Opcode::I_INST_W:
switch (func3) {
case 0: return "ADDIW";
case 1: return "SLLIW";
case 5: return func7 ? "SRAIW" : "SRLIW";
default:
std::abort();
}
case Opcode::SYS_INST:
switch (func3) {
case 0:
switch (imm) {
case 0x000: return "ECALL";
case 0x001: return "EBREAK";
case 0x002: return "URET";
case 0x102: return "SRET";
case 0x302: return "MRET";
default:
std::abort();
}
case 1: return "CSRRW";
case 2: return "CSRRS";
case 3: return "CSRRC";
case 5: return "CSRRWI";
case 6: return "CSRRSI";
case 7: return "CSRRCI";
default:
std::abort();
}
case Opcode::FENCE: return "FENCE";
case Opcode::FL:
switch (func3) {
case 0x1: return "VL";
case 0x2: return "FLW";
case 0x3: return "FLD";
default:
std::abort();
}
case Opcode::FS:
switch (func3) {
case 0x1: return "VS";
case 0x2: return "FSW";
case 0x3: return "FSD";
default:
std::abort();
}
case Opcode::AMO: {
auto amo_type = func7 >> 2;
switch (func3) {
case 0x2:
switch (amo_type) {
case 0x00: return "AMOADD.W";
case 0x01: return "AMOSWAP.W";
case 0x02: return "LR.W";
case 0x03: return "SC.W";
case 0x04: return "AMOXOR.W";
case 0x08: return "AMOOR.W";
case 0x0c: return "AMOAND.W";
case 0x10: return "AMOMIN.W";
case 0x14: return "AMOMAX.W";
case 0x18: return "AMOMINU.W";
case 0x1c: return "AMOMAXU.W";
default:
std::abort();
}
case 0x3:
switch (amo_type) {
case 0x00: return "AMOADD.D";
case 0x01: return "AMOSWAP.D";
case 0x02: return "LR.D";
case 0x03: return "SC.D";
case 0x04: return "AMOXOR.D";
case 0x08: return "AMOOR.D";
case 0x0c: return "AMOAND.D";
case 0x10: return "AMOMIN.D";
case 0x14: return "AMOMAX.D";
case 0x18: return "AMOMINU.D";
case 0x1c: return "AMOMAXU.D";
default:
std::abort();
}
default:
std::abort();
}
}
case Opcode::FCI:
switch (func7) {
case 0x00: return "FADD.S";
case 0x01: return "FADD.D";
case 0x04: return "FSUB.S";
case 0x05: return "FSUB.D";
case 0x08: return "FMUL.S";
case 0x09: return "FMUL.D";
case 0x0c: return "FDIV.S";
case 0x0d: return "FDIV.D";
case 0x2c: return "FSQRT.S";
case 0x2d: return "FSQRT.D";
case 0x10:
switch (func3) {
case 0: return "FSGNJ.S";
case 1: return "FSGNJN.S";
case 2: return "FSGNJX.S";
default:
std::abort();
}
case 0x11:
switch (func3) {
case 0: return "FSGNJ.D";
case 1: return "FSGNJN.D";
case 2: return "FSGNJX.D";
default:
std::abort();
}
case 0x14:
switch (func3) {
case 0: return "FMIN.S";
case 1: return "FMAX.S";
default:
std::abort();
}
case 0x15:
switch (func3) {
case 0: return "FMIN.D";
case 1: return "FMAX.D";
default:
std::abort();
}
case 0x20: return "FCVT.S.D";
case 0x21: return "FCVT.D.S";
case 0x50:
switch (func3) {
case 0: return "FLE.S";
case 1: return "FLT.S";
case 2: return "FEQ.S";
default:
std::abort();
}
case 0x51:
switch (func3) {
case 0: return "FLE.D";
case 1: return "FLT.D";
case 2: return "FEQ.D";
default:
std::abort();
}
case 0x60:
switch (rs2) {
case 0: return "FCVT.W.S";
case 1: return "FCVT.WU.S";
case 2: return "FCVT.L.S";
case 3: return "FCVT.LU.S";
default:
std::abort();
}
case 0x61:
switch (rs2) {
case 0: return "FCVT.W.D";
case 1: return "FCVT.WU.D";
case 2: return "FCVT.L.D";
case 3: return "FCVT.LU.D";
default:
std::abort();
}
case 0x68:
switch (rs2) {
case 0: return "FCVT.S.W";
case 1: return "FCVT.S.WU";
case 2: return "FCVT.S.L";
case 3: return "FCVT.S.LU";
default:
std::abort();
}
case 0x69:
switch (rs2) {
case 0: return "FCVT.D.W";
case 1: return "FCVT.D.WU";
case 2: return "FCVT.D.L";
case 3: return "FCVT.D.LU";
default:
std::abort();
}
case 0x70: return func3 ? "FCLASS.S" : "FMV.X.S";
case 0x71: return func3 ? "FCLASS.D" : "FMV.X.D";
case 0x78: return "FMV.S.X";
case 0x79: return "FMV.D.X";
default:
std::abort();
}
case Opcode::FMADD: return func2 ? "FMADD.D" : "FMADD.S";
case Opcode::FMSUB: return func2 ? "FMSUB.D" : "FMSUB.S";
case Opcode::FMNMADD: return func2 ? "FNMADD.D" : "FNMADD.S";
case Opcode::FMNMSUB: return func2 ? "FNMSUB.D" : "FNMSUB.S";
case Opcode::VSET: return "VSET";
case Opcode::EXT1:
switch (func7) {
case 0:
switch (func3) {
case 0: return "TMC";
case 1: return "WSPAWN";
case 2: return "SPLIT";
case 3: return "JOIN";
case 4: return "BAR";
case 5: return "PRED";
default:
std::abort();
}
default:
std::abort();
}
case Opcode::EXT2:
switch (func3) {
case 1: {
switch (func2) {
case 0: return "CMOV";
default:
std::abort();
}
}
default:
std::abort();
}
default:
std::abort();
}
}
namespace vortex {
std::ostream &operator<<(std::ostream &os, const Instr &instr) {
auto opcode = instr.getOpcode();
auto func3 = instr.getFunc3();
os << op_string(instr);
int sep = 0;
if (instr.getRDType() != RegType::None) {
if (sep++ != 0) { os << ", "; } else { os << " "; }
os << instr.getRDType() << std::dec << instr.getRDest();
}
for (uint32_t i = 0; i < instr.getNRSrc(); ++i) {
if (instr.getRSType(i) == RegType::None)
continue;
if (sep++ != 0) { os << ", "; } else { os << " "; }
os << instr.getRSType(i) << std::dec << instr.getRSrc(i);
}
if (instr.hasImm()) {
if (sep++ != 0) { os << ", "; } else { os << " "; }
os << "0x" << std::hex << instr.getImm();
}
if (opcode == Opcode::SYS_INST && func3 >= 5) {
// CSRs with immediate values
if (sep++ != 0) { os << ", "; } else { os << " "; }
os << "0x" << std::hex << instr.getRSrc(0);
}
return os;
}
}
Decoder::Decoder(const Arch&) {}
std::shared_ptr<Instr> Decoder::decode(uint32_t code) const {
auto instr = std::make_shared<Instr>();
auto op = Opcode((code >> shift_opcode) & mask_opcode);
instr->setOpcode(op);
auto func2 = (code >> shift_func2) & mask_func2;
auto func3 = (code >> shift_func3) & mask_func3;
auto func6 = (code >> shift_func6) & mask_func6;
auto func7 = (code >> shift_func7) & mask_func7;
auto rd = (code >> shift_rd) & mask_reg;
auto rs1 = (code >> shift_rs1) & mask_reg;
auto rs2 = (code >> shift_rs2) & mask_reg;
auto rs3 = (code >> shift_rs3) & mask_reg;
auto op_it = sc_instTable.find(op);
if (op_it == sc_instTable.end()) {
std::cout << std::hex << "Error: invalid opcode: 0x" << op << std::endl;
return nullptr;
}
auto iType = op_it->second;
if (op == Opcode::FL || op == Opcode::FS) {
if (func3 != 0x2 && func3 != 0x3) {
iType = InstType::V_TYPE;
}
}
switch (iType) {
case InstType::R_TYPE:
switch (op) {
case Opcode::FCI:
switch (func7) {
case 0x2c: // FSQRT.S
case 0x2d: // FSQRT.D
instr->setDestReg(rd, RegType::Float);
instr->addSrcReg(rs1, RegType::Float);
break;
case 0x50: // FLE.S, FLT.S, FEQ.S
case 0x51: // FLE.D, FLT.D, FEQ.D
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Float);
instr->addSrcReg(rs2, RegType::Float);
break;
case 0x60: // FCVT.W.D, FCVT.WU.D, FCVT.L.D, FCVT.LU.D
case 0x61: // FCVT.WU.S, FCVT.W.S, FCVT.L.S, FCVT.LU.S
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Float);
instr->addSrcReg(rs2, RegType::None);
break;
case 0x68: // FCVT.S.W, FCVT.S.WU, FCVT.S.L, FCVT.S.LU
case 0x69: // FCVT.D.W, FCVT.D.WU, FCVT.D.L, FCVT.D.LU
instr->setDestReg(rd, RegType::Float);
instr->addSrcReg(rs1, RegType::Integer);
instr->addSrcReg(rs2, RegType::None);
break;
case 0x70: // FCLASS.S, FMV.X.S
case 0x71: // FCLASS.D, FMV.X.D
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Float);
break;
case 0x78: // FMV.S.X
case 0x79: // FMV.D.X
instr->setDestReg(rd, RegType::Float);
instr->addSrcReg(rs1, RegType::Integer);
break;
default:
instr->setDestReg(rd, RegType::Float);
instr->addSrcReg(rs1, RegType::Float);
instr->addSrcReg(rs2, RegType::Float);
break;
}
break;
case Opcode::EXT1:
switch (func7) {
case 0:
switch (func3) {
case 0: // TMC
case 3: // JOIN
instr->addSrcReg(rs1, RegType::Integer);
break;
case 1: // WSPAWN
case 4: // BAR
case 5: // PRED
instr->addSrcReg(rs1, RegType::Integer);
instr->addSrcReg(rs2, RegType::Integer);
break;
case 2: // SPLIT
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Integer);
break;
default:
std::abort();
}
break;
default:
std::abort();
}
break;
default:
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Integer);
instr->addSrcReg(rs2, RegType::Integer);
break;
}
instr->setFunc3(func3);
instr->setFunc7(func7);
break;
case InstType::I_TYPE: {
instr->addSrcReg(rs1, RegType::Integer);
if (op == Opcode::FL) {
instr->setDestReg(rd, RegType::Float);
} else {
instr->setDestReg(rd, RegType::Integer);
}
instr->setFunc3(func3);
instr->setFunc7(func7);
switch (op) {
case Opcode::SYS_INST:
if (func3 != 0) {
// RV32I: CSR
if (func3 >= 5) {
// rs1 holds zimm
instr->setSrcReg(0, rs1, RegType::None);
}
} else {
instr->setDestReg(rd, RegType::None);
instr->setSrcReg(0, rs1, RegType::None);
}
// uint12
instr->setImm(code >> shift_rs2);
break;
case Opcode::FENCE:
// uint12
instr->setImm(code >> shift_rs2);
instr->setDestReg(rd, RegType::None);
instr->setSrcReg(0, rs1, RegType::None);
break;
case Opcode::I_INST:
case Opcode::I_INST_W:
if (func3 == 0x1 || func3 == 0x5) {
auto shamt = rs2; // uint5
#if (XLEN == 64)
if (op == Opcode::I_INST) {
// uint6
shamt |= ((func7 & 0x1) << 5);
}
#endif
instr->setImm(shamt);
} else {
// int12
auto imm = code >> shift_rs2;
instr->setImm(sext(imm, width_i_imm));
}
break;
default:
// int12
auto imm = code >> shift_rs2;
instr->setImm(sext(imm, width_i_imm));
break;
}
} break;
case InstType::S_TYPE: {
instr->addSrcReg(rs1, RegType::Integer);
if (op == Opcode::FS) {
instr->addSrcReg(rs2, RegType::Float);
} else {
instr->addSrcReg(rs2, RegType::Integer);
}
instr->setFunc3(func3);
auto imm = (func7 << width_reg) | rd;
instr->setImm(sext(imm, width_i_imm));
} break;
case InstType::B_TYPE: {
instr->addSrcReg(rs1, RegType::Integer);
instr->addSrcReg(rs2, RegType::Integer);
instr->setFunc3(func3);
auto bit_11 = rd & 0x1;
auto bits_4_1 = rd >> 1;
auto bit_10_5 = func7 & 0x3f;
auto bit_12 = func7 >> 6;
auto imm = (bits_4_1 << 1) | (bit_10_5 << 5) | (bit_11 << 11) | (bit_12 << 12);
instr->setImm(sext(imm, width_i_imm+1));
} break;
case InstType::U_TYPE: {
instr->setDestReg(rd, RegType::Integer);
auto imm = code >> shift_func3;
instr->setImm(sext(imm, width_j_imm));
} break;
case InstType::J_TYPE: {
instr->setDestReg(rd, RegType::Integer);
auto unordered = code >> shift_func3;
auto bits_19_12 = unordered & 0xff;
auto bit_11 = (unordered >> 8) & 0x1;
auto bits_10_1 = (unordered >> 9) & 0x3ff;
auto bit_20 = (unordered >> 19) & 0x1;
auto imm = (bits_10_1 << 1) | (bit_11 << 11) | (bits_19_12 << 12) | (bit_20 << 20);
instr->setImm(sext(imm, width_j_imm+1));
} break;
case InstType::V_TYPE:
switch (op) {
case Opcode::VSET: {
instr->setDestReg(rd, RegType::Vector);
instr->addSrcReg(rs1, RegType::Vector);
instr->setFunc3(func3);
if (func3 == 7) {
instr->setImm(!(code >> shift_vset));
if (instr->getImm()) {
auto immed = (code >> shift_rs2) & mask_v_imm;
instr->setImm(immed);
instr->setVlmul(immed & 0x3);
instr->setVediv((immed >> 4) & 0x3);
instr->setVsew((immed >> 2) & 0x3);
} else {
instr->addSrcReg(rs2, RegType::Vector);
}
} else {
instr->addSrcReg(rs2, RegType::Vector);
instr->setVmask((code >> shift_func7) & 0x1);
instr->setFunc6(func6);
}
} break;
case Opcode::FL:
instr->setDestReg(rd, RegType::Vector);
instr->addSrcReg(rs1, RegType::Vector);
instr->setVlsWidth(func3);
instr->addSrcReg(rs2, RegType::Vector);
instr->setVmask(code >> shift_func7);
instr->setVmop((code >> shift_vmop) & mask_func3);
instr->setVnf((code >> shift_vnf) & mask_func3);
break;
case Opcode::FS:
instr->setVs3(rd);
instr->addSrcReg(rs1, RegType::Vector);
instr->setVlsWidth(func3);
instr->addSrcReg(rs2, RegType::Vector);
instr->setVmask(code >> shift_func7);
instr->setVmop((code >> shift_vmop) & mask_func3);
instr->setVnf((code >> shift_vnf) & mask_func3);
break;
default:
std::abort();
}
break;
case R4_TYPE:
if (op == Opcode::EXT2) {
switch (func3) {
case 1:
switch (func2) {
case 0: // CMOV
instr->setDestReg(rd, RegType::Integer);
instr->addSrcReg(rs1, RegType::Integer);
instr->addSrcReg(rs2, RegType::Integer);
instr->addSrcReg(rs3, RegType::Integer);
break;
default:
std::abort();
}
break;
default:
std::abort();
}
} else {
instr->setDestReg(rd, RegType::Float);
instr->addSrcReg(rs1, RegType::Float);
instr->addSrcReg(rs2, RegType::Float);
instr->addSrcReg(rs3, RegType::Float);
}
instr->setFunc2(func2);
instr->setFunc3(func3);
break;
default:
std::abort();
}
return instr;
}