Implement VU pipelines

note: this commit can be reverted if pipeline emulation is found to be unneccessary
- added MipsPipeline
- implemented VU pipelines for the interpreter
This commit is contained in:
hch12907 2018-09-17 17:18:51 +08:00
parent 1d1212e57e
commit 56b5e45d84
9 changed files with 466 additions and 176 deletions

View file

@ -22,6 +22,7 @@ set(COMMON_SRC_FILES
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Mips/MipsCoprocessor0.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Mips/MipsInstruction.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Mips/MipsInstructionInfo.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Mips/MipsPipeline.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Mips/MmuAccess.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Primitive.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Common/Types/Register/ByteRegister.hpp"
@ -192,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/VuPipelines.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuRegisters.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuState.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuUnitRegisters.cpp"

View file

@ -7,4 +7,5 @@ struct MipsInstructionInfo
const char* const mnemonic; // A string representation of the instruction or subclass.
const int impl_index; // Implementation index.
const int cpi; // Cycles per instruction, used for timing.
const int pipeline; // Pipeline used by the instruction, VU only.
};

View file

@ -0,0 +1,48 @@
#pragma once
#include "Common/Types/Primitive.hpp"
/// The base class for representing the MIPS pipeline.
struct MipsPipeline
{
MipsPipeline(uhword cycles = 0, uhword reg = 0) :
finish_in(cycles),
using_register(reg)
{
}
public:
/// Consumes specified cycle. The amount of cycles to be consumed is 1 by
/// default.
void consume_cycle(uhword cycles = 1)
{
finish_in -= cycles;
}
/// Flushes the pipeline, returning the amount of cycles it would had taken
/// to finish the pipeline
uhword flush_pipeline()
{
uhword cycles_consumed = finish_in;
finish_in = 0;
return cycles_consumed;
}
/// Checks whether the pipeline is still running.
bool is_running() const
{
return finish_in > 0;
}
virtual bool is_using_register(uhword reg) const
{
return using_register == reg;
}
protected:
// The cycles it takes to finish the pipeline execution
uhword finish_in;
// The register that is being used by the pipeline
uhword using_register;
};

View file

@ -12,13 +12,14 @@ CVuInterpreter::CVuInterpreter(Core* core) :
int CVuInterpreter::time_step(const int ticks_available)
{
RResources& r = core->get_resources();
// TODO: Not yet implemented.
#if defined(BUILD_DEBUG)
DEBUG_LOOP_COUNTER++;
#endif
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)
@ -31,23 +32,22 @@ int CVuInterpreter::time_step(const int ticks_available)
// PC & Instructions stuff...
const uword pc = unit->pc.read_uword() & 0x0FFF;
const udword raw_inst = r.ee.bus.read_uword(BusContext::Vu, 0x11000000 + 0x8000 * unit->core_id + pc);
const udword raw_inst = r.ee.bus.read_uword(BusContext::Vu, 0x11000000 | (0x8000 * unit->core_id + pc));
const uword upper_raw_inst = (raw_inst >> 31) & 0xFFFFFFFF;
const uword upper_raw_inst = (raw_inst >> 32) & 0xFFFFFFFF;
const VuInstruction upper_inst = VuInstruction(upper_raw_inst);
const MipsInstructionInfo upper_info = upper_inst.upper_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();
// If I (bit 63) is set, execute UpperInst and LOI (using LowerInst as an immediate)
if ((raw_inst >> 63) & 1)
{
(this->*VU_INSTRUCTION_TABLE[upper_inst.upper_lookup().impl_index])(unit, upper_inst);
execute_upper_instruction(unit, upper_inst);
this->LOI(unit, lower_inst);
// Onto the next unit
// Advance PC and onto the next unit
unit->bdelay.advance_pc(unit->pc);
continue;
}
@ -56,7 +56,7 @@ int CVuInterpreter::time_step(const int ticks_available)
// Here we setup a delay slot for the next instruction
if ((raw_inst >> 62) & 1)
{
if (unit->bdelay.is_branch_pending())
if (unit->bdelay.is_branch_pending())
{
BOOST_LOG(Core::get_logger()) << "Found E-bit in branch delay slot";
}
@ -97,8 +97,8 @@ int CVuInterpreter::time_step(const int ticks_available)
}
}
(this->*VU_INSTRUCTION_TABLE[lower_inst.lower_lookup().impl_index])(unit, lower_inst);
(this->*VU_INSTRUCTION_TABLE[upper_inst.upper_lookup().impl_index])(unit, upper_inst);
execute_lower_instruction(unit, lower_inst);
execute_upper_instruction(unit, upper_inst);
// Advance the PC
unit->bdelay.advance_pc(unit->pc);
@ -107,3 +107,140 @@ int CVuInterpreter::time_step(const int ticks_available)
// TODO: Correct CPI
return 1;
}
int CVuInterpreter::execute_lower_instruction(VuUnit_Base* unit, VuInstruction inst)
{
const MipsInstructionInfo info = inst.lower_lookup();
switch (info.pipeline)
{
case VuPipeline::FDIV:
{
if (!unit->fdiv.is_running())
{
// 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);
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())
{
// 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);
break;
}
else
{
unit->efu.consume_cycle(1);
if (!unit->efu.is_running()) unit->p = unit->efu.new_p;
}
};
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);
unit->ialu.consume_cycle(1);
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");
}
// Every step runs only 1 cycle except when the pipeline stalls
return 1;
}
int CVuInterpreter::execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst)
{
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
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 (!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());
(this->*VU_INSTRUCTION_TABLE[info.impl_index])(unit, inst);
}
else
{
fmac.consume_cycle(1);
}
}
// Every step runs only 1 cycle except when the pipeline stalls
return 1;
}

View file

@ -381,4 +381,8 @@ public:
&CVuInterpreter::XTOP,
&CVuInterpreter::XITOP
};
private:
int execute_upper_instruction(VuUnit_Base* unit, VuInstruction inst);
int execute_lower_instruction(VuUnit_Base* unit, VuInstruction inst);
};

View file

@ -1,4 +1,5 @@
#include "Resources/Ee/Vpu/Vu/VuInstruction.hpp"
#include "Resources/Ee/Vpu/Vu/VuPipelines.hpp"
MipsInstructionInfo VU_INSTRUCTION_TABLE[Constants::EE::VPU::VU::NUMBER_VU_INSTRUCTIONS] =
{
@ -20,174 +21,180 @@ MipsInstructionInfo VU_INSTRUCTION_TABLE[Constants::EE::VPU::VU::NUMBER_VU_INSTR
// Instructions prefixed with "I" utilizes the IALU unit. While calculation
// is done in 1 cycle, there are 2 dummy stages (made to follow the FMACs),
// which shows up in macro mode (normally the results are bypassed)
// Branch instructions prefixed with "I" uses IALU too, but since the results
// are bypassed and not used by CFC2, they are instead unified with other branch
// instructions.
// Some instructions (such as WAITP) are related to FDIV/EFU/IALU pipeline,
// but they actually follow the basic pipeline...
// Lower Instructions
{"LQ", 109, 6},
{"SQ", 112, 6},
{"ILW", 115, 6},
{"ISW", 116, 6},
{"IADDIU", 100, 6},
{"ISUBIU", 104, 6},
{"FCEQ", 132, 6},
{"FCSET", 134, 6},
{"FCAND", 131, 6},
{"FCOR", 133, 6},
{"FSEQ", 125, 6},
{"FSSET", 127, 6},
{"FSAND", 124, 6},
{"FSOR", 126, 6},
{"FMEQ", 129, 6},
{"FMAND", 128, 6},
{"FMOR", 130, 6},
{"FCGET", 135, 6},
{"B", 142, 4},
{"BAL", 143, 4},
{"JR", 144, 4},
{"JALR", 145, 4},
{"IBEQ", 136, 4},
{"IBNE", 141, 4},
{"IBLTZ", 140, 4},
{"IBGTZ", 138, 4},
{"IBLEZ", 139, 4},
{"IBGEZ", 137, 4},
{"IADD", 98, 6},
{"ISUB", 103, 6},
{"IADDI", 99, 6},
{"IAND", 101, 6},
{"IOR", 102, 6},
{"MOVE", 105, 6},
{"LQI", 111, 6},
{"DIV", 95, 9},
{"MTIR", 107, 6},
{"RNEXT", 121, 6},
{"MFP", 146, 6},
{"XTOP", 162, 6},
{"XGKICK", 161, 6},
{"ESADD", 148, 13},
{"EATANxy", 152, 56},
{"ESQRT", 156, 14},
{"ESIN", 158, 31},
{"MR32", 108, 6},
{"SQI", 114, 6},
{"SQRT", 96, 9},
{"MFIR", 106, 6},
{"RGET", 120, 6},
{"XITOP", 163, 6},
{"ERSADD", 149, 20},
{"EATANxz", 153, 56},
{"ERSQRT", 157, 20},
{"EATAN", 159, 56},
{"LQD", 110, 6},
{"RSQRT", 97, 15},
{"ILWR", 117, 6},
{"RINIT", 119, 6},
{"ELENG", 150, 20},
{"ESUM", 154, 14},
{"ERCPR", 155, 14},
{"EEXP", 160, 46},
{"SQD", 113, 6},
{"WAITQ", 123, 6},
{"ISWR", 118, 6},
{"RXOR", 122, 6},
{"ERLENG", 151, 26},
{"WAITP", 147, 6},
{"LQ", 109, 6, VuPipeline::Basic},
{"SQ", 112, 6, VuPipeline::Basic},
{"ILW", 115, 6, VuPipeline::Basic},
{"ISW", 116, 6, VuPipeline::Basic},
{"IADDIU", 100, 6, VuPipeline::IALU},
{"ISUBIU", 104, 6, VuPipeline::IALU},
{"FCEQ", 132, 6, VuPipeline::Basic},
{"FCSET", 134, 6, VuPipeline::Basic},
{"FCAND", 131, 6, VuPipeline::Basic},
{"FCOR", 133, 6, VuPipeline::Basic},
{"FSEQ", 125, 6, VuPipeline::Basic},
{"FSSET", 127, 6, VuPipeline::Basic},
{"FSAND", 124, 6, VuPipeline::Basic},
{"FSOR", 126, 6, VuPipeline::Basic},
{"FMEQ", 129, 6, VuPipeline::Basic},
{"FMAND", 128, 6, VuPipeline::Basic},
{"FMOR", 130, 6, VuPipeline::Basic},
{"FCGET", 135, 6, VuPipeline::Basic},
{"B", 142, 4, VuPipeline::Basic},
{"BAL", 143, 4, VuPipeline::Basic},
{"JR", 144, 4, VuPipeline::Basic},
{"JALR", 145, 4, VuPipeline::Basic},
{"IBEQ", 136, 4, VuPipeline::Basic},
{"IBNE", 141, 4, VuPipeline::Basic},
{"IBLTZ", 140, 4, VuPipeline::Basic},
{"IBGTZ", 138, 4, VuPipeline::Basic},
{"IBLEZ", 139, 4, VuPipeline::Basic},
{"IBGEZ", 137, 4, VuPipeline::Basic},
{"IADD", 98, 6, VuPipeline::IALU},
{"ISUB", 103, 6, VuPipeline::IALU},
{"IADDI", 99, 6, VuPipeline::IALU},
{"IAND", 101, 6, VuPipeline::IALU},
{"IOR", 102, 6, VuPipeline::IALU},
{"MOVE", 105, 6, VuPipeline::Basic},
{"LQI", 111, 6, VuPipeline::Basic},
{"DIV", 95, 9, VuPipeline::FDIV},
{"MTIR", 107, 6, VuPipeline::Basic},
{"RNEXT", 121, 6, VuPipeline::Basic},
{"MFP", 146, 6, VuPipeline::Basic},
{"XTOP", 162, 6, VuPipeline::Basic},
{"XGKICK", 161, 6, VuPipeline::Basic},
{"ESADD", 148, 13, VuPipeline::EFU},
{"EATANxy", 152, 56, VuPipeline::EFU},
{"ESQRT", 156, 14, VuPipeline::EFU},
{"ESIN", 158, 31, VuPipeline::EFU},
{"MR32", 108, 6, VuPipeline::Basic},
{"SQI", 114, 6, VuPipeline::Basic},
{"SQRT", 96, 9, VuPipeline::FDIV},
{"MFIR", 106, 6, VuPipeline::Basic},
{"RGET", 120, 6, VuPipeline::Basic},
{"XITOP", 163, 6, VuPipeline::Basic},
{"ERSADD", 149, 20, VuPipeline::EFU},
{"EATANxz", 153, 56, VuPipeline::EFU},
{"ERSQRT", 157, 20, VuPipeline::EFU},
{"EATAN", 159, 56, VuPipeline::EFU},
{"LQD", 110, 6, VuPipeline::Basic},
{"RSQRT", 97, 15, VuPipeline::FDIV},
{"ILWR", 117, 6, VuPipeline::Basic},
{"RINIT", 119, 6, VuPipeline::Basic},
{"ELENG", 150, 20, VuPipeline::EFU},
{"ESUM", 154, 14, VuPipeline::EFU},
{"ERCPR", 155, 14, VuPipeline::EFU},
{"EEXP", 160, 46, VuPipeline::EFU},
{"SQD", 113, 6, VuPipeline::Basic},
{"WAITQ", 123, 6, VuPipeline::FDIV},
{"ISWR", 118, 6, VuPipeline::Basic},
{"RXOR", 122, 6, VuPipeline::Basic},
{"ERLENG", 151, 26, VuPipeline::EFU},
{"WAITP", 147, 6, VuPipeline::EFU},
// Upper Instructions
{"ADDbc_0", 4, 6},
{"ADDbc_1", 5, 6},
{"ADDbc_2", 6, 6},
{"ADDbc_3", 7, 6},
{"SUBbc_0", 18, 6},
{"SUBbc_1", 19, 6},
{"SUBbc_2", 20, 6},
{"SUBbc_3", 21, 6},
{"MADDbc_0", 46, 6},
{"MADDbc_1", 47, 6},
{"MADDbc_2", 48, 6},
{"MADDbc_3", 49, 6},
{"MSUBbc_0", 60, 6},
{"MSUBbc_1", 61, 6},
{"MSUBbc_2", 62, 6},
{"MSUBbc_3", 63, 6},
{"MAXbc_0", 73, 6},
{"MAXbc_1", 74, 6},
{"MAXbc_2", 75, 6},
{"MAXbc_3", 76, 6},
{"MINIbc_0", 79, 6},
{"MINIbc_1", 80, 6},
{"MINIbc_2", 81, 6},
{"MINIbc_3", 82, 6},
{"MULbc_0", 32, 6},
{"MULbc_1", 33, 6},
{"MULbc_2", 34, 6},
{"MULbc_3", 35, 6},
{"MULq", 31, 6},
{"MAXi", 72, 6},
{"MULi", 30, 6},
{"MINIi", 78, 6},
{"ADDq", 3, 6},
{"MADDq", 45, 6},
{"ADDi", 2, 6},
{"MADDi", 44, 6},
{"SUBq", 17, 6},
{"MSUBq", 59, 6},
{"SUBi", 16, 6},
{"MSUBi", 58, 6},
{"ADD", 1, 6},
{"MADD", 43, 6},
{"MUL", 29, 6},
{"MAX", 71, 6},
{"SUB", 15, 6},
{"MSUB", 57, 6},
{"OPMSUB", 84, 6},
{"MINI", 77, 6},
{"ADDAbc_0", 11, 6},
{"SUBAbc_0", 25, 6},
{"MADDAbc_0", 53, 6},
{"MSUBAbc_0", 67, 6},
{"ITOF0", 90, 6},
{"FTOI0", 86, 6},
{"MULAbc_0", 39, 6},
{"MULAq", 38, 6},
{"ADDAq", 10, 6},
{"SUBAq", 24, 6},
{"ADDA", 8, 6},
{"SUBA", 22, 6},
{"ADDAbc_1", 12, 6},
{"SUBAbc_1", 26, 6},
{"MADDAbc_1", 54, 6},
{"MSUBAbc_1", 68, 6},
{"ITOF4", 91, 6},
{"FTOI4", 87, 6},
{"MULAbc_1", 40, 6},
{"ABS", 0, 6},
{"MADDAq", 52, 6},
{"MSUBAq", 66, 6},
{"MADDA", 50, 6},
{"MSUBA", 64, 6},
{"ADDAbc_2", 13, 6},
{"SUBAbc_2", 27, 6},
{"MADDAbc_2", 55, 6},
{"MSUBAbc_2", 69, 6},
{"ITOF12", 92, 6},
{"FTOI12", 88, 6},
{"MULAbc_2", 41, 6},
{"MULAi", 37, 6},
{"ADDAi", 9, 6},
{"SUBAi", 23, 6},
{"MULA", 36, 6},
{"OPMULA", 83, 6},
{"ADDAbc_3", 14, 6},
{"SUBAbc_3", 28, 6},
{"MADDAbc_3", 56, 6},
{"MSUBAbc_3", 70, 6},
{"ITOF15", 93, 6},
{"FTOI15", 89, 6},
{"MULAbc_3", 42, 6},
{"CLIP", 94, 6},
{"MADDAi", 51, 6},
{"MSUBAi", 65, 6},
{"NOP", 85, 6}};
{"ADDbc_0", 4, 6, VuPipeline::FMAC},
{"ADDbc_1", 5, 6, VuPipeline::FMAC},
{"ADDbc_2", 6, 6, VuPipeline::FMAC},
{"ADDbc_3", 7, 6, VuPipeline::FMAC},
{"SUBbc_0", 18, 6, VuPipeline::FMAC},
{"SUBbc_1", 19, 6, VuPipeline::FMAC},
{"SUBbc_2", 20, 6, VuPipeline::FMAC},
{"SUBbc_3", 21, 6, VuPipeline::FMAC},
{"MADDbc_0", 46, 6, VuPipeline::FMAC},
{"MADDbc_1", 47, 6, VuPipeline::FMAC},
{"MADDbc_2", 48, 6, VuPipeline::FMAC},
{"MADDbc_3", 49, 6, VuPipeline::FMAC},
{"MSUBbc_0", 60, 6, VuPipeline::FMAC},
{"MSUBbc_1", 61, 6, VuPipeline::FMAC},
{"MSUBbc_2", 62, 6, VuPipeline::FMAC},
{"MSUBbc_3", 63, 6, VuPipeline::FMAC},
{"MAXbc_0", 73, 6, VuPipeline::FMAC},
{"MAXbc_1", 74, 6, VuPipeline::FMAC},
{"MAXbc_2", 75, 6, VuPipeline::FMAC},
{"MAXbc_3", 76, 6, VuPipeline::FMAC},
{"MINIbc_0", 79, 6, VuPipeline::FMAC},
{"MINIbc_1", 80, 6, VuPipeline::FMAC},
{"MINIbc_2", 81, 6, VuPipeline::FMAC},
{"MINIbc_3", 82, 6, VuPipeline::FMAC},
{"MULbc_0", 32, 6, VuPipeline::FMAC},
{"MULbc_1", 33, 6, VuPipeline::FMAC},
{"MULbc_2", 34, 6, VuPipeline::FMAC},
{"MULbc_3", 35, 6, VuPipeline::FMAC},
{"MULq", 31, 6, VuPipeline::FMAC},
{"MAXi", 72, 6, VuPipeline::FMAC},
{"MULi", 30, 6, VuPipeline::FMAC},
{"MINIi", 78, 6, VuPipeline::FMAC},
{"ADDq", 3, 6, VuPipeline::FMAC},
{"MADDq", 45, 6, VuPipeline::FMAC},
{"ADDi", 2, 6, VuPipeline::FMAC},
{"MADDi", 44, 6, VuPipeline::FMAC},
{"SUBq", 17, 6, VuPipeline::FMAC},
{"MSUBq", 59, 6, VuPipeline::FMAC},
{"SUBi", 16, 6, VuPipeline::FMAC},
{"MSUBi", 58, 6, VuPipeline::FMAC},
{"ADD", 1, 6, VuPipeline::FMAC},
{"MADD", 43, 6, VuPipeline::FMAC},
{"MUL", 29, 6, VuPipeline::FMAC},
{"MAX", 71, 6, VuPipeline::FMAC},
{"SUB", 15, 6, VuPipeline::FMAC},
{"MSUB", 57, 6, VuPipeline::FMAC},
{"OPMSUB", 84, 6, VuPipeline::FMAC},
{"MINI", 77, 6, VuPipeline::FMAC},
{"ADDAbc_0", 11, 6, VuPipeline::FMAC},
{"SUBAbc_0", 25, 6, VuPipeline::FMAC},
{"MADDAbc_0", 53, 6, VuPipeline::FMAC},
{"MSUBAbc_0", 67, 6, VuPipeline::FMAC},
{"ITOF0", 90, 6, VuPipeline::FMAC},
{"FTOI0", 86, 6, VuPipeline::FMAC},
{"MULAbc_0", 39, 6, VuPipeline::FMAC},
{"MULAq", 38, 6, VuPipeline::FMAC},
{"ADDAq", 10, 6, VuPipeline::FMAC},
{"SUBAq", 24, 6, VuPipeline::FMAC},
{"ADDA", 8, 6, VuPipeline::FMAC},
{"SUBA", 22, 6, VuPipeline::FMAC},
{"ADDAbc_1", 12, 6, VuPipeline::FMAC},
{"SUBAbc_1", 26, 6, VuPipeline::FMAC},
{"MADDAbc_1", 54, 6, VuPipeline::FMAC},
{"MSUBAbc_1", 68, 6, VuPipeline::FMAC},
{"ITOF4", 91, 6, VuPipeline::FMAC},
{"FTOI4", 87, 6, VuPipeline::FMAC},
{"MULAbc_1", 40, 6, VuPipeline::FMAC},
{"ABS", 0, 6, VuPipeline::FMAC},
{"MADDAq", 52, 6, VuPipeline::FMAC},
{"MSUBAq", 66, 6, VuPipeline::FMAC},
{"MADDA", 50, 6, VuPipeline::FMAC},
{"MSUBA", 64, 6, VuPipeline::FMAC},
{"ADDAbc_2", 13, 6, VuPipeline::FMAC},
{"SUBAbc_2", 27, 6, VuPipeline::FMAC},
{"MADDAbc_2", 55, 6, VuPipeline::FMAC},
{"MSUBAbc_2", 69, 6, VuPipeline::FMAC},
{"ITOF12", 92, 6, VuPipeline::FMAC},
{"FTOI12", 88, 6, VuPipeline::FMAC},
{"MULAbc_2", 41, 6, VuPipeline::FMAC},
{"MULAi", 37, 6, VuPipeline::FMAC},
{"ADDAi", 9, 6, VuPipeline::FMAC},
{"SUBAi", 23, 6, VuPipeline::FMAC},
{"MULA", 36, 6, VuPipeline::FMAC},
{"OPMULA", 83, 6, VuPipeline::FMAC},
{"ADDAbc_3", 14, 6, VuPipeline::FMAC},
{"SUBAbc_3", 28, 6, VuPipeline::FMAC},
{"MADDAbc_3", 56, 6, VuPipeline::FMAC},
{"MSUBAbc_3", 70, 6, VuPipeline::FMAC},
{"ITOF15", 93, 6, VuPipeline::FMAC},
{"FTOI15", 89, 6, VuPipeline::FMAC},
{"MULAbc_3", 42, 6, VuPipeline::FMAC},
{"CLIP", 94, 6, VuPipeline::FMAC},
{"MADDAi", 51, 6, VuPipeline::FMAC},
{"MSUBAi", 65, 6, VuPipeline::FMAC},
{"NOP", 85, 6, VuPipeline::FMAC}};
MipsInstructionInfo VuInstruction::lower_lookup() const
{

View file

@ -0,0 +1 @@
#include "Resources/Ee/Vpu/Vu/VuPipelines.hpp"

View file

@ -0,0 +1,82 @@
#pragma once
#include "Common/Types/Mips/MipsPipeline.hpp"
#include "Common/Types/Register/SizedWordRegister.hpp"
struct VuPipeline
{
enum {
FMAC = 0,
FDIV = 1,
EFU = 2,
IALU = 3,
Basic = 4, // LSU, 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.
struct FmacPipeline : public MipsPipeline
{
FmacPipeline(uhword cycles = 0, uhword reg = 0, ubyte field = 0) :
MipsPipeline(cycles, (reg << 4) | field)
{
}
bool is_using_register(uhword reg) const override
{
return is_using_register(reg >> 4, reg & 0b1111);
}
bool is_using_register(ubyte reg, size_t field) const
{
const uhword using_reg = using_register >> 4;
const uhword reg_field = using_register & 0b1111;
// 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));
}
};
/// The FDIV pipeline.
///
/// When a DIV instruction is executed while the pipeline is still running, a
/// resource hazard is generated and the instruction is stalled until the write-back
/// stage. Furthermore, when a Upper Instruction uses the Q register while the
/// pipeline is still running, FMAC will not stall and will use the original
/// Q register instead.
struct FdivPipeline : public MipsPipeline
{
using MipsPipeline::MipsPipeline;
SizedWordRegister new_q;
};
/// The EFU pipeline.
///
/// The EFU pipeline is pretty similar to the FDIV pipeline, except that the
/// instruction is stalled until the last stage of the execution, not write-back.
/// (NOTE: the next instruction starts during the write-back stage!)
struct EfuPipeline : public MipsPipeline
{
using MipsPipeline::MipsPipeline;
SizedWordRegister new_p;
};
/// The IALU pipeline.
///
/// IALU takes only one cycle to execute, but there are 2 dummy stages needed
/// to go through before the result is written back to the registers, though
/// the result can bypass directly to other pipelines and stalls do not occur
/// except when it is transferred with CFC2.
struct IaluPipeline : public MipsPipeline
{
using MipsPipeline::MipsPipeline;
};

View file

@ -13,6 +13,7 @@
#include "Common/Types/Register/SizedHwordRegister.hpp"
#include "Common/Types/Register/SizedQwordRegister.hpp"
#include "Resources/Ee/Vpu/Vu/VuBranchDelaySlot.hpp"
#include "Resources/Ee/Vpu/Vu/VuPipelines.hpp"
#include "Resources/Ee/Vpu/Vu/VuState.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnitRegisters.hpp"
@ -82,6 +83,12 @@ public:
/// VU operation state. The VU has 3 operation states: Ready, Run, Stop.
VuOperationState operation_state;
/// VU pipelines.
FmacPipeline fmac[4];
FdivPipeline fdiv;
EfuPipeline efu;
IaluPipeline ialu;
public:
template<class Archive>
void serialize(Archive & archive)