mirror of
https://github.com/hch12907/orbum.git
synced 2024-05-20 05:00:48 -04:00
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:
parent
56b5e45d84
commit
52e6bf9d99
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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__;
|
||||
|
|
|
@ -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},
|
||||
|
|
273
liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.cpp
Normal file
273
liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.cpp
Normal 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;
|
||||
}
|
91
liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.hpp
Normal file
91
liborbum/src/Resources/Ee/Vpu/Vu/VuInstructionDecoder.hpp
Normal 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;
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
FdivPipeline fdiv;
|
||||
EfuPipeline efu;
|
||||
IaluPipeline ialu;
|
||||
LsuPipeline lsu;
|
||||
|
||||
public:
|
||||
template<class Archive>
|
||||
|
|
Loading…
Reference in a new issue