mirror of
https://github.com/hch12907/orbum.git
synced 2024-06-01 19:08:05 -04:00
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:
parent
1d1212e57e
commit
56b5e45d84
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
};
|
48
liborbum/src/Common/Types/Mips/MipsPipeline.hpp
Normal file
48
liborbum/src/Common/Types/Mips/MipsPipeline.hpp
Normal 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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
1
liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.cpp
Normal file
1
liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "Resources/Ee/Vpu/Vu/VuPipelines.hpp"
|
82
liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.hpp
Normal file
82
liborbum/src/Resources/Ee/Vpu/Vu/VuPipelines.hpp
Normal 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;
|
||||
};
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue