Implement data hazard, register writing priority, proper CPI for VU interpreter

- implemented data hazard
- implemented register writing priority
- proper CPI
- note: WAITP and WAITQ is handled as a data hazard (special case)
This commit is contained in:
hch12907 2018-09-29 01:10:26 +08:00
parent 56b5e45d84
commit 52e6bf9d99
10 changed files with 624 additions and 126 deletions

View file

@ -193,6 +193,8 @@ set(COMMON_SRC_FILES
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuBranchDelaySlot.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstruction.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstruction.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuRegisters.hpp"

View file

@ -36,7 +36,8 @@ public:
virtual bool is_using_register(uhword reg) const
{
return using_register == reg;
// R0 is hardwired to 0.
return reg != 0 && using_register == reg;
}
protected:

View file

@ -1,4 +1,5 @@
#include <boost/format.hpp>
#include <variant>
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
@ -18,10 +19,6 @@ int CVuInterpreter::time_step(const int ticks_available)
RResources& r = core->get_resources();
int cycles_consumed[2] = { 1, 1 };
// The priority of register writing is: COP2 Transfer > Upper Inst > Lower Inst
for (auto* unit : r.ee.vpu.vu.units)
{
// Move on if the unit is not running and the delay slot is empty
@ -36,14 +33,18 @@ int CVuInterpreter::time_step(const int ticks_available)
const uword upper_raw_inst = (raw_inst >> 32) & 0xFFFFFFFF;
const VuInstruction upper_inst = VuInstruction(upper_raw_inst);
const MipsInstructionInfo upper_info = upper_inst.lower_lookup();
const uword lower_raw_inst = raw_inst & 0xFFFFFFFF;
const VuInstruction lower_inst = VuInstruction(lower_raw_inst);
const MipsInstructionInfo lower_info = lower_inst.lower_lookup();
const VuInstructionDecoder decoder = VuInstructionDecoder(lower_inst, upper_inst);
// If I (bit 63) is set, execute UpperInst and LOI (using LowerInst as an immediate)
if ((raw_inst >> 63) & 1)
{
execute_upper_instruction(unit, upper_inst);
execute_upper_instruction(unit, upper_inst, upper_info, decoder);
this->LOI(unit, lower_inst);
// Advance PC and onto the next unit
@ -97,150 +98,265 @@ int CVuInterpreter::time_step(const int ticks_available)
}
}
execute_lower_instruction(unit, lower_inst);
execute_upper_instruction(unit, upper_inst);
// Register writing priority, if both upper and lower inst write to the same
// register, the priority is: COP2 Transfer > Upper Inst > Lower Inst
try
{
// Try obtaining the destination (will throw if the instruction writes to non-VF/VI regs)
const uword upper_dest = *decoder.upper_dest();
const uword lower_dest = *decoder.lower_dest();
// Check if the lower instruction write to VI or VF
// If it writes to VF, check if it writes to the same reg
if (decoder.decode_lower().field != VuDecodedInst::Int)
{
if (upper_dest == lower_dest)
{
SizedQwordRegister original_vf = unit->vf[upper_dest];
execute_lower_instruction(unit, upper_inst, upper_info, decoder);
// The result produced by lower instruction is discarded
unit->vf[upper_dest] = original_vf;
execute_upper_instruction(unit, lower_inst, lower_info, decoder);
}
}
else
{
// Otherwise just run it usually
execute_upper_instruction(unit, upper_inst, upper_info, decoder);
execute_lower_instruction(unit, lower_inst, lower_info, decoder);
}
}
catch (std::exception)
{
// If one of them write to special regs (P, Q, etc), execute like usual
execute_upper_instruction(unit, upper_inst, upper_info, decoder);
execute_lower_instruction(unit, lower_inst, lower_info, decoder);
}
// Advance the PC
unit->bdelay.advance_pc(unit->pc);
if (!check_data_hazard(unit, decoder))
unit->bdelay.advance_pc(unit->pc);
}
// TODO: Correct CPI
return 1;
}
int CVuInterpreter::execute_lower_instruction(VuUnit_Base* unit, VuInstruction inst)
int CVuInterpreter::execute_lower_instruction(VuUnit_Base* unit, const VuInstruction inst, MipsInstructionInfo info, const VuInstructionDecoder& decoder)
{
const MipsInstructionInfo info = inst.lower_lookup();
unit->efu.consume_cycle(1);
unit->fdiv.consume_cycle(1);
unit->ialu.consume_cycle(1);
unit->lsu.consume_cycle(1);
// If the units have finished execution, replace the original regs with new ones
if (!unit->efu.is_running()) unit->p = unit->efu.new_p;
if (!unit->fdiv.is_running()) unit->q = unit->fdiv.new_q;
// If there's a data hazard, stall
if (check_data_hazard(unit, decoder)) return 1;
switch (info.pipeline)
{
case VuPipeline::FDIV:
{
if (!unit->fdiv.is_running())
case VuPipeline::EFU:
{
// FDIV is not responsible for the first 2 cycles (fetch instruction
// and read registers)
unit->fdiv = FdivPipeline(info.cpi - 2);
// Temporarily save current Q in new_q, and swap it later
// (needed because the instructions operate on unit->q)
unit->fdiv.new_q = unit->q;
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
std::swap(unit->q, unit->fdiv.new_q);
if (!unit->efu.is_running())
{
// The 3 cycles: Fetch, obtain VPU register, write-back. As the
// EFU handles only the execution stage, other cycles are ignored
unit->efu = EfuPipeline(info.cpi - 3);
// Store original unit->p in new_p temporarily before executing the
// instruction (since it operates on unit->p) and swap it back later.
std::swap(unit->p, unit->efu.new_p);
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
std::swap(unit->p, unit->efu.new_p);
}
break;
}
else
{
// Run the pipelines, and use the new registers if the pipeline is finished
// Same for EFU etc
unit->fdiv.consume_cycle(1);
if (!unit->fdiv.is_running()) unit->q = unit->fdiv.new_q;
}
};
case VuPipeline::EFU:
{
if (!unit->efu.is_running())
case VuPipeline::FDIV:
{
// The 3 cycles are for instruction fetching, and registers read/write.
// EFU is responsible for the execution stages only.
unit->efu = EfuPipeline(info.cpi - 3);
// Temporarily save current P in new_p, and swap it later
// (needed because the instructions operate on unit->p)
unit->efu.new_p = unit->p;
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
std::swap(unit->p, unit->efu.new_p);
if (!unit->fdiv.is_running())
{
// The 2 cycles: Fetch, obtain VPU register. As the FDIV handles
// both execution & write-back stage, other cycles are ignored
unit->fdiv = FdivPipeline(info.cpi - 2);
// See EFU.
std::swap(unit->q, unit->fdiv.new_q);
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
std::swap(unit->q, unit->fdiv.new_q);
}
break;
}
else
case VuPipeline::IALU:
{
unit->efu.consume_cycle(1);
if (!unit->efu.is_running()) unit->p = unit->efu.new_p;
if (!unit->ialu.is_running())
{
// Try to get the destination
int dest = decoder.lower_dest().value_or(0);
// See FDIV
unit->ialu = IaluPipeline(info.cpi - 2, dest);
}
// The results are bypassed to other instructions directly, so
// there's no need to swap stuff around
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
break;
}
};
case VuPipeline::IALU:
{
// See case EFU for the 3 cycles.
// TODO: if an integer register is used during store/load data hazard is generated
unit->ialu = IaluPipeline(info.cpi - 3);
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
case VuPipeline::LSU:
{
if (!unit->lsu.is_running())
{
int dest = decoder.lower_dest().value_or(0);
unit->lsu = LsuPipeline(info.cpi - 2, dest);
}
unit->ialu.consume_cycle(1);
break;
};
break;
}
case VuPipeline::Basic:
{
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
break;
};
default:
throw std::runtime_error("VU: Found instruction of unsupported pipeline");
case VuPipeline::Basic:
{
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
}
}
// Every step runs only 1 cycle except when the pipeline stalls
return 1;
}
int CVuInterpreter::execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst)
int CVuInterpreter::execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst, MipsInstructionInfo info, const VuInstructionDecoder& decoder)
{
const MipsInstructionInfo info = inst.upper_lookup();
for (FmacPipeline& fmac : unit->fmac)
{
// TODO: Data hazards occur when a register is being used by two
// units/pipelines, but the current implementation tracks only
fmac.consume_cycle(1);
}
bool is_type_3 = (inst.opcode() >> 2) == 0b1111;
// If the VF register is being used, stall FMAC
{
// Upper Instruction field type 3 specifies only 2 registers, fs and ft,
// while the others specifies 3 registers, fd, fs and ft.
// (type 3 has the opcode 1111__)
if (is_type_3)
{
if (fmac.is_using_register(inst.ft(), inst.dest()) ||
fmac.is_using_register(inst.fs(), inst.dest()))
{
fmac.consume_cycle(1);
continue;
}
}
else
{
if (fmac.is_using_register(inst.fd(), inst.dest()) ||
fmac.is_using_register(inst.fs(), inst.dest()) ||
fmac.is_using_register(inst.ft(), inst.dest()))
{
fmac.consume_cycle(1);
continue;
}
}
}
// If there's a data hazard, stall
if (check_data_hazard(unit, decoder)) return 1;
for (FmacPipeline& fmac : unit->fmac)
{
if (!fmac.is_running())
{
// FMAC has three execution stages, fixed for all the FMAC instructions
if (is_type_3)
fmac = FmacPipeline(3, inst.ft(), inst.dest());
else
fmac = FmacPipeline(3, inst.fd(), inst.dest());
fmac = FmacPipeline(info.cpi - 2, decoder.upper_dest().value_or(0), inst.dest());
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
}
else
{
fmac.consume_cycle(1);
break;
}
}
// Every step runs only 1 cycle except when the pipeline stalls
return 1;
}
bool CVuInterpreter::check_data_hazard(VuUnit_Base* unit, const VuInstructionDecoder& decoder) const
{
// Obtain the registers to be read by the instruction
// If the instruction does not specify the field(s), use 0 as placeholder,
// as VF0/VI0 is hardwired to 0 (and the manual did this too)
const int upper_read[3] = {
decoder.upper_src(0).value_or(0),
decoder.upper_src(1).value_or(0),
decoder.upper_src(2).value_or(-1) // this one's special because only MADD/MSUB uses this
};
const int lower_read[2] = {
decoder.lower_src(0).value_or(-1),
decoder.lower_src(1).value_or(-1)
};
// If the instruction is WAITP, return true if EFU is running
if ((decoder.get_lower_inst().value) & 0x7FF == 0x7BF)
{
if (unit->efu.is_running()) return true;
}
// If the instruction is WAITQ, return true if FDIV is running
if ((decoder.get_lower_inst().value) & 0x7FF == 0x3BF)
{
if (unit->efu.is_running()) return true;
}
// Upper Instructions data hazard check
// Special case: if the instruction is MADD/MSUB/OPMSUB, then BC relates to 3rd field instead of 2nd
// note: (1 << (3 - inst.bc())) converts BC to DEST
{
VuInstruction inst = decoder.get_upper_inst();
if (upper_read[2] == -1)
{
if (decoder.decode_upper().field == VuDecodedInst::Bc)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(upper_read[0], inst.dest())) return true;
if (fmac.is_using_register(upper_read[1], 1 << (3 - inst.bc()))) return true;
}
}
else if (decoder.decode_upper().field == VuDecodedInst::Dest)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(upper_read[0], inst.dest())) return true;
if (fmac.is_using_register(upper_read[1], inst.dest())) return true;
}
}
}
else
{
if (decoder.decode_upper().field == VuDecodedInst::Bc)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(upper_read[0], inst.dest())) return true;
if (fmac.is_using_register(upper_read[1], inst.dest())) return true;
if (fmac.is_using_register(upper_read[2], 1 << (3 - inst.bc()))) return true;
}
}
else if (decoder.decode_upper().field == VuDecodedInst::Dest)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(upper_read[0], inst.dest())) return true;
if (fmac.is_using_register(upper_read[1], inst.dest())) return true;
if (fmac.is_using_register(upper_read[2], inst.dest())) return true;
}
}
}
}
// Lower Instructions data hazard check
{
VuInstruction inst = decoder.get_upper_inst();
if (decoder.decode_lower().field == VuDecodedInst::FsfFtf)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(lower_read[0], inst.fsf())) return true;
if (fmac.is_using_register(lower_read[1], inst.ftf())) return true;
}
}
else if (decoder.decode_lower().field == VuDecodedInst::Dest)
{
for (FmacPipeline& fmac : unit->fmac)
{
if (fmac.is_using_register(lower_read[0], inst.dest())) return true;
if (fmac.is_using_register(lower_read[1], inst.dest())) return true;
}
}
else if (decoder.decode_lower().field == VuDecodedInst::Int)
{
if (unit->lsu.is_using_register(lower_read[0])) return true;
}
}
return false;
}

View file

@ -2,6 +2,7 @@
#include "Controller/Ee/Vpu/Vu/CVu.hpp"
#include "Resources/Ee/Vpu/Vu/VuInstruction.hpp"
#include "Resources/Ee/Vpu/Vu/VuInstructionDecoder.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnits.hpp"
class Core;
@ -383,6 +384,8 @@ public:
};
private:
int execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst);
int execute_lower_instruction(VuUnit_Base* unit, VuInstruction inst);
bool check_data_hazard(VuUnit_Base* unit, const VuInstructionDecoder& decoder) const;
int execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst, MipsInstructionInfo info, const VuInstructionDecoder& decoder);
int execute_lower_instruction(VuUnit_Base* unit, VuInstruction inst, MipsInstructionInfo info, const VuInstructionDecoder& decoder);
};

View file

@ -92,9 +92,7 @@ void CVuInterpreter::RXOR(VuUnit_Base* unit, const VuInstruction inst)
void CVuInterpreter::WAITQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO:
// Actual synchronization.
// The VU Interpreter is synchronous, I imagine, so synchronization is actually unneeded.
// WAITQ is handled as a special case in the interpreter
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITQ is called!") % __FILENAME__ % __LINE__;
@ -228,9 +226,7 @@ void CVuInterpreter::JALR(VuUnit_Base* unit, const VuInstruction inst)
void CVuInterpreter::WAITP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO:
// Actual synchronization.
// The VU Interpreter is synchronous, I imagine, so synchronization is actually unneeded.
// WAITP is handled as a special case in the interpreter
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITP is called!") % __FILENAME__ % __LINE__;

View file

@ -31,7 +31,7 @@ MipsInstructionInfo VU_INSTRUCTION_TABLE[Constants::EE::VPU::VU::NUMBER_VU_INSTR
// Lower Instructions
{"LQ", 109, 6, VuPipeline::Basic},
{"SQ", 112, 6, VuPipeline::Basic},
{"ILW", 115, 6, VuPipeline::Basic},
{"ILW", 115, 6, VuPipeline::LSU},
{"ISW", 116, 6, VuPipeline::Basic},
{"IADDIU", 100, 6, VuPipeline::IALU},
{"ISUBIU", 104, 6, VuPipeline::IALU},
@ -86,7 +86,7 @@ MipsInstructionInfo VU_INSTRUCTION_TABLE[Constants::EE::VPU::VU::NUMBER_VU_INSTR
{"EATAN", 159, 56, VuPipeline::EFU},
{"LQD", 110, 6, VuPipeline::Basic},
{"RSQRT", 97, 15, VuPipeline::FDIV},
{"ILWR", 117, 6, VuPipeline::Basic},
{"ILWR", 117, 6, VuPipeline::LSU},
{"RINIT", 119, 6, VuPipeline::Basic},
{"ELENG", 150, 20, VuPipeline::EFU},
{"ESUM", 154, 14, VuPipeline::EFU},

View file

@ -0,0 +1,273 @@
#include "Resources/Ee/Vpu/Vu/VuInstructionDecoder.hpp"
/// Each VuInstruction specifies 3 registers (one for the destination reg
/// and others for source) and uses 4 at most (eg MADD/MSUB). In this table index 0
/// stores the destination reg, while index 1, 2 & 3 stores the source reg(s).
/// If the destination bitfield is set to nullopt, then it is assumed that the
/// instruction uses special registers such as P, Q or ACC.
VuDecodedInst VU_DECODE_TABLE[Constants::EE::VPU::VU::NUMBER_VU_INSTRUCTIONS] {
// DESTINATION SOURCE (1) SOURCE (2) SOURCE (3) FIELDS
// Upper Instructions
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ABS
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // ADD
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // ADDi
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // ADDq
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDbc_0
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDbc_1
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDbc_2
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // ADDA
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // ADDAi
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // ADDAq
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDAbc_0
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDAbc_1
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDAbc_2
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // ADDAbc_3
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // SUB
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // SUBi
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // SUBq
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBbc_0
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBbc_1
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBbc_2
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // SUBA
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // SUBAi
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // SUBAq
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBAbc_0
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBAbc_1
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBAbc_2
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // SUBAbc_3
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // MUL
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // MULi
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // MULq
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULbc_0
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULbc_1
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULbc_2
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // MULA
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // MULAi
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, std::nullopt, VuDecodedInst::Dest), // MULAq
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULAbc_0
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULAbc_1
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULAbc_2
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MULAbc_3
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Dest), // MADD
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, VuDecodedInst::Dest), // MADDi
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, VuDecodedInst::Dest), // MADDq
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDbc_0
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDbc_1
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDbc_2
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Dest), // MADDA
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, VuDecodedInst::Dest), // MADDAi
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, VuDecodedInst::Dest), // MADDAq
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDAbc_0
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDAbc_1
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDAbc_2
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MADDAbc_3
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Dest), // MSUB
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, VuDecodedInst::Dest), // MSUBi
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, VuDecodedInst::Dest), // MSUBq
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBbc_0
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBbc_1
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBbc_2
VuDecodedInst(VuInstruction::FD, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Dest), // MSUBA
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::I, VuDecodedInst::Dest), // MSUBAi
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuSpecialRegs::Q, VuDecodedInst::Dest), // MSUBAq
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBAbc_0
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBAbc_1
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBAbc_2
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Bc), // MSUBAbc_3
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // MAX
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // MAXi
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MAXbc_0
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MAXbc_1
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MAXbc_2
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MAXbc_3
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // MINI
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuSpecialRegs::I, std::nullopt, VuDecodedInst::Dest), // MINIi
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MINIbc_0
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MINIbc_1
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MINIbc_2
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Bc), // MINIbc_3
VuDecodedInst(VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // OPMULA
VuDecodedInst(VuSpecialRegs::ACC, VuSpecialRegs::ACC, VuInstruction::FS, VuInstruction::FT, VuDecodedInst::Dest), // OPMSUB
VuDecodedInst(std::nullopt, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Dest), // NOP
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // FTOI0
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // FTOI4
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // FTOI12
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // FTOI15
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ITOF0
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ITOF4
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ITOF12
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ITOF15
VuDecodedInst(VuSpecialRegs::CLIP, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Dest), // CLIP
// Lower Instructions
VuDecodedInst(VuSpecialRegs::Q, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::FsfFtf), // DIV
VuDecodedInst(VuSpecialRegs::Q, VuInstruction::FT, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // SQRT
VuDecodedInst(VuSpecialRegs::Q, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::FsfFtf), // RSQRT
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Int), // IADD
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // IADDI
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // IADDIU
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Int), // IAND
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Int), // IOR
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, VuInstruction::FT, std::nullopt, VuDecodedInst::Int), // ISUB
VuDecodedInst(VuInstruction::FD, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // ISUBI
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // MOVE
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // MFIR
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // MTIR
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // MR32
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // LQ
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // LQD
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // LQI
VuDecodedInst(VuInstruction::FS, VuInstruction::FT, std::nullopt, std::nullopt, VuDecodedInst::Dest), // SQ
VuDecodedInst(VuInstruction::FS, VuInstruction::FT, std::nullopt, std::nullopt, VuDecodedInst::Dest), // SQD
VuDecodedInst(VuInstruction::FS, VuInstruction::FT, std::nullopt, std::nullopt, VuDecodedInst::Dest), // SQI
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // ILW
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // ISW
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // ILWR
VuDecodedInst(VuInstruction::FT, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // ISWR
VuDecodedInst(VuSpecialRegs::R, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // RINIT
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::R, std::nullopt, std::nullopt, VuDecodedInst::Dest), // RGET
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::R, std::nullopt, std::nullopt, VuDecodedInst::Dest), // RNEXT
VuDecodedInst(VuSpecialRegs::R, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // RXOR
VuDecodedInst(std::nullopt, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Dest), // WAITQ
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::SF, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FSAND
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::SF, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FSEQ
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::SF, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FSOR
VuDecodedInst(VuSpecialRegs::SF, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FSSET
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::MAC, VuInstruction::FS, std::nullopt, VuDecodedInst::Int ), // FMAND
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::MAC, VuInstruction::FS, std::nullopt, VuDecodedInst::Int ), // FMEQ
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::MAC, VuInstruction::FS, std::nullopt, VuDecodedInst::Int ), // FMOR
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::CLIP, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FCAND
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::CLIP, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FCEQ
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::CLIP, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FCOR
VuDecodedInst(VuSpecialRegs::CLIP, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FCSET
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::CLIP, std::nullopt, std::nullopt, VuDecodedInst::Int ), // FCGET
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FT, VuInstruction::FS, std::nullopt, VuDecodedInst::Int ), // IBEQ
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int ), // IBGEZ
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int ), // IBGTZ
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int ), // IBLEZ
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int ), // IBLTZ
VuDecodedInst(VuSpecialRegs::PC, VuInstruction::FT, VuInstruction::FS, std::nullopt, VuDecodedInst::Int ), // IBNE
VuDecodedInst(VuSpecialRegs::PC, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // B
VuDecodedInst(VuInstruction::FT, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // BAL
VuDecodedInst(VuSpecialRegs::PC, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // J
VuDecodedInst(VuInstruction::FT, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int ), // JALR
VuDecodedInst(VuInstruction::FT, VuSpecialRegs::P, std::nullopt, std::nullopt, VuDecodedInst::Dest), // MFP
VuDecodedInst(std::nullopt, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Dest), // WAITP
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ESADD
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ERSADD
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ELENG
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ERLENG
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // EATANxy
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // EATANxz
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Dest), // ESUM
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // ERCPR
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // ESQRT
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // ERSQRT
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // ESIN
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // EATAN
VuDecodedInst(VuSpecialRegs::P, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::FsfFtf), // EEXP
VuDecodedInst(std::nullopt, VuInstruction::FS, std::nullopt, std::nullopt, VuDecodedInst::Int), // XGKICK
VuDecodedInst(VuInstruction::FT, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int), // XTOP
VuDecodedInst(VuInstruction::FT, std::nullopt, std::nullopt, std::nullopt, VuDecodedInst::Int), // XITOP
};
VuInstructionDecoder::VuInstructionDecoder(VuInstruction lower, VuInstruction upper) :
lower_inst(lower),
upper_inst(upper),
decoded_inst_lower(VU_DECODE_TABLE[lower.lower_lookup().impl_index]),
decoded_inst_upper(VU_DECODE_TABLE[lower.upper_lookup().impl_index])
{
}
const VuDecodedInst& VuInstructionDecoder::decode_lower() const
{
return decoded_inst_lower;
}
const VuDecodedInst& VuInstructionDecoder::decode_upper() const
{
return decoded_inst_upper;
}
const VuInstruction& VuInstructionDecoder::get_upper_inst() const
{
return upper_inst;
}
const VuInstruction& VuInstructionDecoder::get_lower_inst() const
{
return lower_inst;
}
std::optional<int> VuInstructionDecoder::upper_dest() const
{
if (decoded_inst_upper.dest_reg.has_value())
{
VuDecodeInfo value = decoded_inst_upper.dest_reg.value();
if (Bitfield* ptr = std::get_if<Bitfield>(&value))
{
return ptr->extract_from(upper_inst.value);
}
}
return std::nullopt;
}
std::optional<int> VuInstructionDecoder::upper_src(int index) const
{
const std::optional<VuDecodeInfo>* src_regs[3] = {
&decoded_inst_upper.source_reg_1,
&decoded_inst_upper.source_reg_2,
&decoded_inst_upper.source_reg_3
};
if (src_regs[index]->has_value())
{
VuDecodeInfo value = src_regs[index]->value();
if (Bitfield* ptr = std::get_if<Bitfield>(&value))
{
return ptr->extract_from(upper_inst.value);
}
}
return std::nullopt;
}
std::optional<int> VuInstructionDecoder::lower_dest() const
{
if (decoded_inst_lower.dest_reg.has_value())
{
VuDecodeInfo value = decoded_inst_lower.dest_reg.value();
if (Bitfield* ptr = std::get_if<Bitfield>(&value))
{
return ptr->extract_from(lower_inst.value);
}
}
return std::nullopt;
}
std::optional<int> VuInstructionDecoder::lower_src(int index) const
{
const std::optional<VuDecodeInfo>* src_regs[3] = {
&decoded_inst_lower.source_reg_1,
&decoded_inst_lower.source_reg_2,
&decoded_inst_lower.source_reg_3
};
if (src_regs[index]->has_value())
{
VuDecodeInfo value = src_regs[index]->value();
if (auto ptr = std::get_if<Bitfield>(&value))
{
return ptr->extract_from(lower_inst.value);
}
}
return std::nullopt;
}

View file

@ -0,0 +1,91 @@
#pragma once
#include <variant>
#include "Common/Types/Bitfield.hpp"
#include "Resources/Ee/Vpu/Vu/VuInstruction.hpp"
enum class VuSpecialRegs
{
ACC,
CLIP,
I,
P,
PC, // program counter
Q,
R,
SF, // sticky flag
MAC // mac flag
};
typedef std::variant<Bitfield, VuSpecialRegs> VuDecodeInfo;
struct VuDecodedInst
{
// Used for storing the field specifier (dest, fsf/ftf, bc, etc..)
// Note: Integer GPRs do not have a field.
enum FieldSpecifier
{
FsfFtf = 0,
Dest = 1,
Bc = 2,
Int = 3,
};
// Default constructor
VuDecodedInst() :
dest_reg(std::nullopt),
source_reg_1(std::nullopt),
source_reg_2(std::nullopt),
source_reg_3(std::nullopt),
field(FieldSpecifier::Dest)
{
}
VuDecodedInst(
std::optional<VuDecodeInfo> dest,
std::optional<VuDecodeInfo> src_1,
std::optional<VuDecodeInfo> src_2,
std::optional<VuDecodeInfo> src_3,
int dest_field
) :
dest_reg(dest),
source_reg_1(src_1),
source_reg_2(src_2),
source_reg_3(src_3),
field(dest_field)
{
}
std::optional<VuDecodeInfo> dest_reg;
std::optional<VuDecodeInfo> source_reg_1;
std::optional<VuDecodeInfo> source_reg_2;
std::optional<VuDecodeInfo> source_reg_3;
int field;
};
class VuInstructionDecoder
{
public:
VuInstructionDecoder(const VuInstruction lower, const VuInstruction upper);
const VuDecodedInst& decode_lower() const;
const VuDecodedInst& decode_upper() const;
const VuInstruction& get_lower_inst() const;
const VuInstruction& get_upper_inst() const;
std::optional<int> lower_dest() const;
std::optional<int> upper_dest() const;
std::optional<int> lower_src(int index) const;
std::optional<int> upper_src(int index) const;
private:
VuInstruction lower_inst;
VuInstruction upper_inst;
VuDecodedInst decoded_inst_lower;
VuDecodedInst decoded_inst_upper;
};

View file

@ -10,19 +10,24 @@ struct VuPipeline
FDIV = 1,
EFU = 2,
IALU = 3,
Basic = 4, // LSU, BRU, RANDU etc follow this. Similar to FMAC, but doesn't use FMAC units
LSU = 4,
Basic = 5, // BRU, RANDU etc follow this. Similar to FMAC, but doesn't use FMAC units
};
};
/// The FMAC pipeline.
///
/// There are in total 4 FMAC units in a VU (each corresponding to a field).
/// Resource hazard is not generated for the FMAC pipeline, only data hazards.
/// When the same field of the same register is accessed by 2 different FMAC units
/// stalling occurs.
/// There are in total 4 FMAC units in a VU, therefore resource hazard is not
/// generated for the FMAC pipeline, only data hazards - when the same field of
/// the same register is accessed by 2 different FMAC units stalling occurs.
struct FmacPipeline : public MipsPipeline
{
// FmacPipeline uses the using_register variable to store the register and its
// field(s) being used by the pipeline.
// The first 4 bits are used for fields, and the 5 bits after it are used
// for registers. (The rest are paddings)
// For example, VF[8]xyz would be stored as [0000000 01000 1110].
FmacPipeline(uhword cycles = 0, uhword reg = 0, ubyte field = 0) :
MipsPipeline(cycles, (reg << 4) | field)
{
@ -40,7 +45,7 @@ struct FmacPipeline : public MipsPipeline
// True if the pipeline is running, is using the same register, and is using the same fields
// Also true if it is VF00 that is being written into (it is hardwired to 0)
return (reg == 0) || (is_running() && (using_reg == reg) && ((reg_field ^ field) != 0b1111));
return is_running() && (using_reg == reg) && ((reg_field ^ field) != 0b1111);
}
};
@ -80,3 +85,13 @@ struct IaluPipeline : public MipsPipeline
{
using MipsPipeline::MipsPipeline;
};
/// The LSU "pipeline".
///
/// Not documentated in the manual, it is responsible for the integer load/store
/// operations. Has a 4-cycle-long latency before the register it uses is available
/// for other instructions to use.
struct LsuPipeline : public MipsPipeline
{
using MipsPipeline::MipsPipeline;
};

View file

@ -88,6 +88,7 @@ public:
FdivPipeline fdiv;
EfuPipeline efu;
IaluPipeline ialu;
LsuPipeline lsu;
public:
template<class Archive>