mirror of
https://github.com/hch12907/orbum.git
synced 2024-05-10 08:44:02 -04:00
VPU: Split VPU0/1 into two threads
- VU and VIF are no longer on separate threads - Instead, they are now on the same "VPU" thread - VPU is the one which is threaded now - VPU0/1 run on separate threads This is done to (hopefully!) improve synchronization
This commit is contained in:
parent
65b1ae5dd2
commit
3b6cc1f701
|
@ -86,6 +86,8 @@ set(COMMON_SRC_FILES
|
|||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vif/CVif_SET.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vif/CVif_TRANSFER.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vif/CVif_UNPACK.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/CVpu.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/CVpu.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/CVu.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/CVu.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.cpp"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
CEeCoreInterpreter::CEeCoreInterpreter(Core* core) :
|
||||
CEeCore(core),
|
||||
c_vu_interpreter(core)
|
||||
c_vu_interpreter(core, 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
0
liborbum/src/Controller/Ee/Vpu/CVpu.cpp
Normal file
0
liborbum/src/Controller/Ee/Vpu/CVpu.cpp
Normal file
75
liborbum/src/Controller/Ee/Vpu/CVpu.hpp
Normal file
75
liborbum/src/Controller/Ee/Vpu/CVpu.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include "Controller/CController.hpp"
|
||||
#include "Controller/ControllerEvent.hpp"
|
||||
#include "Controller/Ee/Vpu/Vif/CVif.hpp"
|
||||
#include "Controller/Ee/Vpu/Vu/CVu.hpp"
|
||||
#include "Core.hpp"
|
||||
|
||||
class Core;
|
||||
|
||||
template<typename VuController>
|
||||
class CVpu : public CController
|
||||
{
|
||||
public:
|
||||
CVpu(Core* core, int id) :
|
||||
CController(core),
|
||||
core_id(id),
|
||||
vif(CVif(core, id)),
|
||||
vu(VuController(core, id))
|
||||
{
|
||||
}
|
||||
|
||||
void handle_event(const ControllerEvent& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ControllerEvent::Type::Time:
|
||||
{
|
||||
int ticks_remaining = time_to_ticks(event.data.time_us);
|
||||
while (ticks_remaining > 0)
|
||||
ticks_remaining -= time_step(ticks_remaining);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::runtime_error("CVpu event handler not implemented - please fix!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the VU and VIF.
|
||||
int time_step(const int ticks_available)
|
||||
{
|
||||
// Run VIF and VU for one cycle.
|
||||
vif.time_step(1);
|
||||
vu.time_step(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Converts a time duration into the number of ticks that would have occurred.
|
||||
int time_to_ticks(const double time_us)
|
||||
{
|
||||
int ticks = static_cast<int>(time_us / 1.0e6 * Constants::EE::VPU::VU::VU_CLK_SPEED * core->get_options().system_bias_vu);
|
||||
|
||||
if (ticks < 10)
|
||||
{
|
||||
static bool warned = false;
|
||||
if (!warned)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "VPU ticks too low - increase time delta";
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
/// The ID of the VPU.
|
||||
int core_id;
|
||||
|
||||
/// VU/VIF executors (can be recompiler or interpreter)
|
||||
CVif vif;
|
||||
VuController vu;
|
||||
};
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
#include "Core.hpp"
|
||||
|
||||
CVif::CVif(Core* core) :
|
||||
CController(core)
|
||||
CVif::CVif(Core* core, int id) :
|
||||
CController(core),
|
||||
core_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -48,80 +49,108 @@ int CVif::time_step(const int ticks_available)
|
|||
{
|
||||
auto& r = core->get_resources();
|
||||
|
||||
for (auto& unit : r.ee.vpu.vif.units)
|
||||
VifUnit_Base* unit = r.ee.vpu.vif.units[core_id];
|
||||
// If STC is written, reset the following fields of VIF.STAT:
|
||||
// VSS, VFS, VIS, INT, ER0, ER1
|
||||
if (unit->fbrst.extract_field(VifUnitRegister_Fbrst::STC))
|
||||
{
|
||||
// If STC is written, reset the following fields of VIF.STAT:
|
||||
// VSS, VFS, VIS, INT, ER0, ER1
|
||||
if (unit->fbrst.extract_field(VifUnitRegister_Fbrst::STC))
|
||||
{
|
||||
uword stat = unit->stat.read_uword();
|
||||
stat = VifUnitRegister_Stat::VSS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::VFS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::VIS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::INT.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::ER0.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::ER1.insert_into(stat, 0u);
|
||||
uword stat = unit->stat.read_uword();
|
||||
stat = VifUnitRegister_Stat::VSS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::VFS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::VIS.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::INT.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::ER0.insert_into(stat, 0u);
|
||||
stat = VifUnitRegister_Stat::ER1.insert_into(stat, 0u);
|
||||
|
||||
unit->stat.write_uword(stat);
|
||||
unit->stat.write_uword(stat);
|
||||
}
|
||||
|
||||
// Check if VIF is stalled, do not do anything (FBRST.STC needs to be written to before we return).
|
||||
if (unit->stat.is_stalled())
|
||||
return 1;
|
||||
|
||||
// If the VIF is waiting for the VU, run the current instruction and call it a day
|
||||
if (unit->stat.extract_field(VifUnitRegister_Stat::VEW))
|
||||
{
|
||||
(this->*INSTRUCTION_TABLE[unit->inst->get_info()->impl_index])(unit, *unit->inst);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check the FIFO queue for incoming DMA packet. Exit early if there is nothing to process.
|
||||
if (!unit->dma_fifo_queue->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
return 1;
|
||||
|
||||
// Four VIF statuses: 0b00 Idle, 01 Waiting for data, 10 Decoding VIFcode, 11 Decompressing data
|
||||
const ubyte status = unit->stat.extract_field(VifUnitRegister_Stat::VPS);
|
||||
|
||||
// Check for STP
|
||||
if (!status && unit->fbrst.extract_field(VifUnitRegister_Fbrst::STP))
|
||||
{
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VSS, 1);
|
||||
unit->fbrst.insert_field(VifUnitRegister_Fbrst::STP, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If RST is written, reset the VIF
|
||||
if (!status && unit->fbrst.extract_field(VifUnitRegister_Fbrst::RST))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "VIF: Resetting VIF" << unit->core_id;
|
||||
unit->fbrst.insert_field(VifUnitRegister_Fbrst::RST, 0);
|
||||
|
||||
// Reinitialize the VIF unit
|
||||
unit->dma_fifo_queue->initialize();
|
||||
*unit = VifUnit_Base(unit->core_id, unit->dma_fifo_queue);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = unit->packet_progress; i < NUMBER_WORDS_IN_QWORD; i++, unit->packet_progress = i)
|
||||
{
|
||||
unit->dma_fifo_queue->read(reinterpret_cast<ubyte*>(&unit->processing_data), NUMBER_BYTES_IN_WORD);
|
||||
|
||||
const uword& data = unit->processing_data;
|
||||
|
||||
// If the VIF is idling, treat the data as a VIFcode
|
||||
if (!status)
|
||||
{
|
||||
unit->code.write_uword(data);
|
||||
unit->inst = std::make_unique<VifcodeInstruction>(data);
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VPS, 0b10);
|
||||
unit->subpackets_left = obtain_required_words(*unit, *unit->inst);
|
||||
BOOST_LOG(Core::get_logger()) << "VIF: Fetched instruction " << unit->inst->get_info()->mnemonic;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if VIF is stalled, do not do anything (FBRST.STC needs to be written to before we continue).
|
||||
if (unit->stat.is_stalled())
|
||||
continue;
|
||||
|
||||
// If the VIF is waiting for the VU, run the current instruction and call it a day
|
||||
if (unit->stat.extract_field(VifUnitRegister_Stat::VEW))
|
||||
else
|
||||
{
|
||||
(this->*INSTRUCTION_TABLE[unit->inst->get_info()->impl_index])(unit, *unit->inst);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the FIFO queue for incoming DMA packet. Exit early if there is nothing to process.
|
||||
if (!unit->dma_fifo_queue->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
continue;
|
||||
|
||||
// Four VIF statuses: 0b00 Idle, 01 Waiting for data, 10 Decoding VIFcode, 11 Decompressing data
|
||||
const ubyte status = unit->stat.extract_field(VifUnitRegister_Stat::VPS);
|
||||
|
||||
// Check for STP
|
||||
if (!status && unit->fbrst.extract_field(VifUnitRegister_Fbrst::STP))
|
||||
{
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VSS, 1);
|
||||
unit->fbrst.insert_field(VifUnitRegister_Fbrst::STP, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If RST is written, reset the VIF
|
||||
if (!status && unit->fbrst.extract_field(VifUnitRegister_Fbrst::RST))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "VIF: Resetting VIF" << unit->core_id;
|
||||
unit->fbrst.insert_field(VifUnitRegister_Fbrst::RST, 0);
|
||||
|
||||
// Reinitialize the VIF unit
|
||||
unit->dma_fifo_queue->initialize();
|
||||
*unit = VifUnit_Base(unit->core_id, unit->dma_fifo_queue);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = unit->packet_progress; i < NUMBER_WORDS_IN_QWORD; i++, unit->packet_progress = i)
|
||||
{
|
||||
unit->dma_fifo_queue->read(reinterpret_cast<ubyte*>(&unit->processing_data), NUMBER_BYTES_IN_WORD);
|
||||
|
||||
const uword& data = unit->processing_data;
|
||||
|
||||
// If the VIF is idling, treat the data as a VIFcode
|
||||
if (!status)
|
||||
// If there are no packets left, set the VIF status to idle
|
||||
if (!unit->subpackets_left)
|
||||
{
|
||||
unit->code.write_uword(data);
|
||||
unit->inst = std::make_unique<VifcodeInstruction>(data);
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VPS, 0b10);
|
||||
unit->subpackets_left = obtain_required_words(*unit, *unit->inst);
|
||||
BOOST_LOG(Core::get_logger()) << "VIF: Fetched instruction " << unit->inst->get_info()->mnemonic;
|
||||
continue;
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VPS, 0b00);
|
||||
// If the VIF is idling, treat the data as a VIFcode
|
||||
if (!status)
|
||||
{
|
||||
unit->code.write_uword(data);
|
||||
unit->inst = std::make_unique<VifcodeInstruction>(data);
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::VPS, 0b10);
|
||||
unit->subpackets_left = obtain_required_words(*unit, *unit->inst);
|
||||
|
||||
// If the VIFcode is valid, set ER1 to 0
|
||||
if (!unit->inst->get_info()->impl_index)
|
||||
{
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::ER1, 0);
|
||||
}
|
||||
|
||||
BOOST_LOG(Core::get_logger()) << "VIF: Fetched instruction " << unit->inst->get_info()->mnemonic;
|
||||
|
||||
// TODO: confirm behaviour
|
||||
// Does it take one whole cycle for the VIF to process a VIFcode?
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unit->subpackets_left--;
|
||||
|
||||
// If there are no packets left, set the VIF status to idle
|
||||
if (!unit->subpackets_left)
|
||||
{
|
||||
|
@ -135,7 +164,7 @@ int CVif::time_step(const int ticks_available)
|
|||
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VIF_KEYS[unit->core_id], 1);
|
||||
}
|
||||
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
|
||||
unit->subpackets_left--;
|
||||
|
@ -143,11 +172,11 @@ int CVif::time_step(const int ticks_available)
|
|||
(this->*INSTRUCTION_TABLE[unit->inst->get_info()->impl_index])(unit, *unit->inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unit->packet_progress >= NUMBER_WORDS_IN_QWORD)
|
||||
{
|
||||
unit->packet_progress = 0;
|
||||
}
|
||||
if (unit->packet_progress >= NUMBER_WORDS_IN_QWORD)
|
||||
{
|
||||
unit->packet_progress = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -209,6 +238,8 @@ void CVif::INSTRUCTION_UNSUPPORTED(VifUnit_Base* unit, const VifcodeInstruction
|
|||
return NOP(unit, inst);
|
||||
}
|
||||
|
||||
unit->stat.insert_field(VifUnitRegister_Stat::ER1, 1);
|
||||
|
||||
throw std::runtime_error("VIFcode CMD field was invalid! Please fix.");
|
||||
}
|
||||
|
||||
|
@ -218,3 +249,19 @@ void CVif::NOP(VifUnit_Base* unit, const VifcodeInstruction inst)
|
|||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CVif::write_vu_mem(VifUnit_Base* unit, const uword offset, const T data)
|
||||
{
|
||||
RResources& r = core->get_resources();
|
||||
ArrayByteMemory& vu_mem = *r.ee.vpu.vu.units[unit->core_id]->vu_mem;
|
||||
|
||||
const uword vu_addr = unit->inst->addr();
|
||||
|
||||
}
|
||||
|
||||
// Implement write_vu_mem(..) only for selected types
|
||||
// Note: the requirement for T is 4 bytes long
|
||||
template void CVif::write_vu_mem<uword>(VifUnit_Base* unit, const uword offset, const uword data);
|
||||
template void CVif::write_vu_mem<sword>(VifUnit_Base* unit, const uword offset, const sword data);
|
||||
template void CVif::write_vu_mem< f32 >(VifUnit_Base* unit, const uword offset, const f32 data);
|
||||
|
|
|
@ -15,7 +15,7 @@ class Core;
|
|||
class CVif : public CController
|
||||
{
|
||||
public:
|
||||
CVif(Core* core);
|
||||
CVif(Core* core, int id);
|
||||
|
||||
void handle_event(const ControllerEvent& event) override;
|
||||
|
||||
|
@ -107,4 +107,11 @@ private:
|
|||
/// Obtains the amount of words (a packet holds 4 subpackets, each subpacket is a
|
||||
/// word long) required for the instruction.
|
||||
int obtain_required_words(const VifUnit_Base& unit, const VifcodeInstruction inst) const;
|
||||
|
||||
/// Stores the ID of the VIF.
|
||||
int core_id;
|
||||
|
||||
/// A helper function for writing data to the VU mem.
|
||||
template<typename T>
|
||||
void write_vu_mem(VifUnit_Base* unit, const uword offset, const T data);
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
void CVif::UNPACK_S_32(VifUnit_Base* unit, const VifcodeInstruction inst)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CVif::UNPACK_S_16(VifUnit_Base* unit, const VifcodeInstruction inst)
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
#include "Resources/RResources.hpp"
|
||||
|
||||
CVu::CVu(Core* core) :
|
||||
CController(core)
|
||||
CVu::CVu(Core* core, int id) :
|
||||
CController(core),
|
||||
core_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -9,14 +9,18 @@ class Core;
|
|||
class CVu : public CController
|
||||
{
|
||||
public:
|
||||
CVu(Core* core);
|
||||
CVu(Core* core, int id);
|
||||
|
||||
void handle_event(const ControllerEvent& event) override;
|
||||
|
||||
/// Steps through the VU core state, executing one macro and one micro instruction.
|
||||
virtual int time_step(const int ticks_available) = 0;
|
||||
|
||||
protected:
|
||||
/// Stores the ID of the VU
|
||||
int core_id;
|
||||
|
||||
private:
|
||||
/// Converts a time duration into the number of ticks that would have occurred.
|
||||
int time_to_ticks(const double time_us);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#include "Core.hpp"
|
||||
#include "Resources/RResources.hpp"
|
||||
|
||||
CVuInterpreter::CVuInterpreter(Core* core) :
|
||||
CVu(core)
|
||||
CVuInterpreter::CVuInterpreter(Core* core, int id) :
|
||||
CVu(core, id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -20,149 +20,148 @@ int CVuInterpreter::time_step(const int ticks_available)
|
|||
|
||||
RResources& r = core->get_resources();
|
||||
|
||||
for (auto* unit : r.ee.vpu.vu.units)
|
||||
VuUnit_Base* const unit = r.ee.vpu.vu.units[core_id];
|
||||
|
||||
// Move on if the unit is not running and the delay slot is empty
|
||||
if (unit->operation_state != VuOperationState::Run && !unit->bdelay.is_branch_pending())
|
||||
{
|
||||
// Move on if the unit is not running and the delay slot is empty
|
||||
if (unit->operation_state != VuOperationState::Run && !unit->bdelay.is_branch_pending())
|
||||
return 1;
|
||||
}
|
||||
|
||||
// PC & Instructions stuff...
|
||||
const uword pc = unit->pc.read_uword() & 0x0FFF;
|
||||
const udword raw_inst = unit->micro_mem->read_uword(pc);
|
||||
|
||||
const uword upper_raw_inst = (raw_inst >> 32) & 0xFFFFFFFF;
|
||||
const VuInstruction upper_inst = VuInstruction(upper_raw_inst);
|
||||
const MipsInstructionInfo upper_info = upper_inst.get_upper_info();
|
||||
const VuInstructionDecoder upper_decoder = VuInstructionDecoder(upper_inst, upper_info);
|
||||
|
||||
const uword lower_raw_inst = raw_inst & 0xFFFFFFFF;
|
||||
const VuInstruction lower_inst = VuInstruction(lower_raw_inst);
|
||||
const MipsInstructionInfo lower_info = lower_inst.get_lower_info();
|
||||
const VuInstructionDecoder lower_decoder = VuInstructionDecoder(lower_inst, lower_info);
|
||||
|
||||
// Flush the pipelines
|
||||
unit->efu.consume_cycle(1);
|
||||
unit->fdiv.consume_cycle(1);
|
||||
unit->ialu.consume_cycle(1);
|
||||
unit->lsu.consume_cycle(1);
|
||||
|
||||
for (FmacPipeline& fmac : unit->fmac)
|
||||
{
|
||||
fmac.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;
|
||||
|
||||
bool data_hazard_occured = check_data_hazard(unit, upper_decoder, lower_decoder);
|
||||
|
||||
// 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_decoder, data_hazard_occured);
|
||||
this->LOI(unit, lower_inst);
|
||||
|
||||
// Advance PC and onto the next cycle
|
||||
unit->bdelay.advance_pc(unit->pc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If E (bit 62) is set, execute current and next instruction, and
|
||||
// terminate the current micro subroutine
|
||||
// Here we setup a delay slot for the next instruction
|
||||
if ((raw_inst >> 62) & 1)
|
||||
{
|
||||
if (unit->bdelay.is_branch_pending())
|
||||
{
|
||||
continue;
|
||||
BOOST_LOG(Core::get_logger()) << "Found E-bit in branch delay slot";
|
||||
}
|
||||
|
||||
// PC & Instructions stuff...
|
||||
const uword pc = unit->pc.read_uword() & 0x0FFF;
|
||||
const udword raw_inst = unit->micro_mem->read_uword(pc);
|
||||
// Don't actually branch
|
||||
unit->bdelay.set_branch_itype(unit->pc, 0);
|
||||
|
||||
const uword upper_raw_inst = (raw_inst >> 32) & 0xFFFFFFFF;
|
||||
const VuInstruction upper_inst = VuInstruction(upper_raw_inst);
|
||||
const MipsInstructionInfo upper_info = upper_inst.get_upper_info();
|
||||
const VuInstructionDecoder upper_decoder = VuInstructionDecoder(upper_inst, upper_info);
|
||||
// Change the state of the VU
|
||||
unit->operation_state = VuOperationState::Ready;
|
||||
}
|
||||
|
||||
const uword lower_raw_inst = raw_inst & 0xFFFFFFFF;
|
||||
const VuInstruction lower_inst = VuInstruction(lower_raw_inst);
|
||||
const MipsInstructionInfo lower_info = lower_inst.get_lower_info();
|
||||
const VuInstructionDecoder lower_decoder = VuInstructionDecoder(lower_inst, lower_info);
|
||||
|
||||
// Flush the pipelines
|
||||
unit->efu.consume_cycle(1);
|
||||
unit->fdiv.consume_cycle(1);
|
||||
unit->ialu.consume_cycle(1);
|
||||
unit->lsu.consume_cycle(1);
|
||||
|
||||
for (FmacPipeline& fmac : unit->fmac)
|
||||
// If M (bit 61) is set, then execute QMTC2 or CTC2 without interlocking
|
||||
// (VU0 only)
|
||||
if (((raw_inst >> 61) & 1) && unit->core_id == 0)
|
||||
{
|
||||
VuUnit_Vu0& vu = r.ee.vpu.vu.unit_0;
|
||||
if (vu.transferred_reg.has_value())
|
||||
{
|
||||
fmac.consume_cycle(1);
|
||||
*vu.ccr[vu.transferred_reg_location] = vu.transferred_reg.value();
|
||||
vu.transferred_reg = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
bool data_hazard_occured = check_data_hazard(unit, upper_decoder, lower_decoder);
|
||||
|
||||
// If I (bit 63) is set, execute UpperInst and LOI (using LowerInst as an immediate)
|
||||
if ((raw_inst >> 63) & 1)
|
||||
// If D (bit 60) and DE (in FBRST) is set, terminate the micro subroutine and interrupt
|
||||
if ((raw_inst >> 60) & 1)
|
||||
{
|
||||
if (r.ee.vpu.vu.fbrst.de(unit->core_id))
|
||||
{
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
this->LOI(unit, lower_inst);
|
||||
|
||||
// Advance PC and onto the next unit
|
||||
unit->bdelay.advance_pc(unit->pc);
|
||||
continue;
|
||||
auto _lock = r.ee.intc.stat.scope_lock();
|
||||
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
|
||||
unit->operation_state = VuOperationState::Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// If E (bit 62) is set, execute current and next instruction, and
|
||||
// terminate the current micro subroutine
|
||||
// Here we setup a delay slot for the next instruction
|
||||
if ((raw_inst >> 62) & 1)
|
||||
// If T (bit 59) and TE (in FBRST) is set, terminate the micro subroutine and interrupt
|
||||
if ((raw_inst >> 59) & 1)
|
||||
{
|
||||
if (r.ee.vpu.vu.fbrst.te(unit->core_id))
|
||||
{
|
||||
if (unit->bdelay.is_branch_pending())
|
||||
auto _lock = r.ee.intc.stat.scope_lock();
|
||||
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
|
||||
unit->operation_state = VuOperationState::Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = *upper_decoder.try_get_dest();
|
||||
const uword lower_dest = *lower_decoder.try_get_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 (!lower_decoder.is_integer_instruction())
|
||||
{
|
||||
if (upper_dest == lower_dest)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "Found E-bit in branch delay slot";
|
||||
}
|
||||
|
||||
// Don't actually branch
|
||||
unit->bdelay.set_branch_itype(unit->pc, 0);
|
||||
|
||||
// Change the state of the VU
|
||||
unit->operation_state = VuOperationState::Ready;
|
||||
}
|
||||
|
||||
// If M (bit 61) is set, then execute QMTC2 or CTC2 without interlocking
|
||||
// (VU0 only)
|
||||
if (((raw_inst >> 61) & 1) && unit->core_id == 0)
|
||||
{
|
||||
VuUnit_Vu0& vu = r.ee.vpu.vu.unit_0;
|
||||
if (vu.transferred_reg.has_value())
|
||||
{
|
||||
*vu.ccr[vu.transferred_reg_location] = vu.transferred_reg.value();
|
||||
vu.transferred_reg = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// If D (bit 60) and DE (in FBRST) is set, terminate the micro subroutine and interrupt
|
||||
if ((raw_inst >> 60) & 1)
|
||||
{
|
||||
if (r.ee.vpu.vu.fbrst.de(unit->core_id))
|
||||
{
|
||||
auto _lock = r.ee.intc.stat.scope_lock();
|
||||
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
|
||||
unit->operation_state = VuOperationState::Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// If T (bit 59) and TE (in FBRST) is set, terminate the micro subroutine and interrupt
|
||||
if ((raw_inst >> 59) & 1)
|
||||
{
|
||||
if (r.ee.vpu.vu.fbrst.te(unit->core_id))
|
||||
{
|
||||
auto _lock = r.ee.intc.stat.scope_lock();
|
||||
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
|
||||
unit->operation_state = VuOperationState::Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = *upper_decoder.try_get_dest();
|
||||
const uword lower_dest = *lower_decoder.try_get_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 (!lower_decoder.is_integer_instruction())
|
||||
{
|
||||
if (upper_dest == lower_dest)
|
||||
{
|
||||
SizedQwordRegister original_vf = unit->vf[upper_dest];
|
||||
execute_lower_instruction(unit, lower_decoder, data_hazard_occured);
|
||||
|
||||
// The result produced by lower instruction is discarded
|
||||
unit->vf[upper_dest] = original_vf;
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise just run it as usual
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
SizedQwordRegister original_vf = unit->vf[upper_dest];
|
||||
execute_lower_instruction(unit, lower_decoder, data_hazard_occured);
|
||||
|
||||
// The result produced by lower instruction is discarded
|
||||
unit->vf[upper_dest] = original_vf;
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
}
|
||||
}
|
||||
catch (std::exception)
|
||||
else
|
||||
{
|
||||
// If one of them write to special regs (P, Q, etc), execute like usual
|
||||
// Otherwise just run it as usual
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
execute_lower_instruction(unit, lower_decoder, data_hazard_occured);
|
||||
}
|
||||
|
||||
// Advance the PC
|
||||
if (!check_data_hazard(unit, upper_decoder, lower_decoder))
|
||||
unit->bdelay.advance_pc(unit->pc);
|
||||
}
|
||||
catch (std::exception)
|
||||
{
|
||||
// If one of them write to special regs (P, Q, etc), execute like usual
|
||||
execute_upper_instruction(unit, upper_decoder, data_hazard_occured);
|
||||
execute_lower_instruction(unit, lower_decoder, data_hazard_occured);
|
||||
}
|
||||
|
||||
// Advance the PC
|
||||
if (!check_data_hazard(unit, upper_decoder, lower_decoder))
|
||||
unit->bdelay.advance_pc(unit->pc);
|
||||
|
||||
// TODO: Correct CPI
|
||||
return 1;
|
||||
|
|
|
@ -11,7 +11,7 @@ class Core;
|
|||
class CVuInterpreter : public CVu
|
||||
{
|
||||
public:
|
||||
CVuInterpreter(Core* core);
|
||||
CVuInterpreter(Core* core, int id);
|
||||
|
||||
/// Steps through the VU core state, executing one macro and one micro instruction.
|
||||
int time_step(const int ticks_available) override;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "Controller/Ee/Intc/CEeIntc.hpp"
|
||||
#include "Controller/Ee/Ipu/CIpu.hpp"
|
||||
#include "Controller/Ee/Timers/CEeTimers.hpp"
|
||||
#include "Controller/Ee/Vpu/Vif/CVif.hpp"
|
||||
#include "Controller/Ee/Vpu/CVpu.hpp"
|
||||
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
|
||||
#include "Controller/Gs/Core/CGsCore.hpp"
|
||||
#include "Controller/Gs/Crtc/CCrtc.hpp"
|
||||
|
@ -50,8 +50,8 @@ CoreOptions CoreOptions::make_default()
|
|||
"",
|
||||
"",
|
||||
"",
|
||||
10,
|
||||
4, //std::thread::hardware_concurrency() - 1,
|
||||
1,
|
||||
8, //std::thread::hardware_concurrency() - 1,
|
||||
|
||||
1.0,
|
||||
1.0,
|
||||
|
@ -132,8 +132,8 @@ Core::Core(const CoreOptions& options) :
|
|||
controllers[ControllerType::Type::EeIntc] = std::make_unique<CEeIntc>(this);
|
||||
controllers[ControllerType::Type::Gif] = std::make_unique<CGif>(this);
|
||||
controllers[ControllerType::Type::Ipu] = std::make_unique<CIpu>(this);
|
||||
controllers[ControllerType::Type::Vif] = std::make_unique<CVif>(this);
|
||||
controllers[ControllerType::Type::Vu] = std::make_unique<CVuInterpreter>(this);
|
||||
controllers[ControllerType::Type::Vif] = std::make_unique<CVpu<CVuInterpreter>>(this, 0);
|
||||
controllers[ControllerType::Type::Vu] = std::make_unique<CVpu<CVuInterpreter>>(this, 1);
|
||||
controllers[ControllerType::Type::IopCore] = std::make_unique<CIopCoreInterpreter>(this);
|
||||
controllers[ControllerType::Type::IopDmac] = std::make_unique<CIopDmac>(this);
|
||||
controllers[ControllerType::Type::IopTimers] = std::make_unique<CIopTimers>(this);
|
||||
|
|
|
@ -23,10 +23,10 @@ public:
|
|||
/// The data being processed by the VIF.
|
||||
uword processing_data;
|
||||
|
||||
/// A packet has 4 subpackets. Which one are we processing now?
|
||||
/// A packet has 4 subpackets (words) - this is nth subpacket we are processing
|
||||
uword packet_progress;
|
||||
|
||||
/// The amount of packets that are needed for processing.
|
||||
/// The amount of packets left for processing.
|
||||
uword subpackets_left;
|
||||
|
||||
/// DMA FIFO queue.
|
||||
|
|
Loading…
Reference in a new issue