mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-07 15:31:26 +12:00
SPU: Implement interrupts handling for remaining events
This commit is contained in:
parent
4ed92f4155
commit
c0c52c33b9
3 changed files with 87 additions and 17 deletions
|
@ -125,7 +125,13 @@ void spu_interpreter::set_interrupt_status(spu_thread& spu, spu_opcode_t op)
|
||||||
|
|
||||||
bool spu_interpreter::STOP(spu_thread& spu, spu_opcode_t op)
|
bool spu_interpreter::STOP(spu_thread& spu, spu_opcode_t op)
|
||||||
{
|
{
|
||||||
if (!spu.stop_and_signal(op.opcode & 0x3fff))
|
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||||
|
|
||||||
|
const bool advance_pc = spu.stop_and_signal(op.opcode & 0x3fff);
|
||||||
|
|
||||||
|
spu.allow_interrupts_in_cpu_work = allow;
|
||||||
|
|
||||||
|
if (!advance_pc)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -166,8 +172,12 @@ bool spu_interpreter::MFSPR(spu_thread& spu, spu_opcode_t op)
|
||||||
|
|
||||||
bool spu_interpreter::RDCH(spu_thread& spu, spu_opcode_t op)
|
bool spu_interpreter::RDCH(spu_thread& spu, spu_opcode_t op)
|
||||||
{
|
{
|
||||||
|
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||||
|
|
||||||
const s64 result = spu.get_ch_value(op.ra);
|
const s64 result = spu.get_ch_value(op.ra);
|
||||||
|
|
||||||
|
spu.allow_interrupts_in_cpu_work = allow;
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -428,7 +438,13 @@ bool spu_interpreter::MTSPR(spu_thread&, spu_opcode_t)
|
||||||
|
|
||||||
bool spu_interpreter::WRCH(spu_thread& spu, spu_opcode_t op)
|
bool spu_interpreter::WRCH(spu_thread& spu, spu_opcode_t op)
|
||||||
{
|
{
|
||||||
if (!spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]))
|
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||||
|
|
||||||
|
const bool advance_pc = spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]);
|
||||||
|
|
||||||
|
spu.allow_interrupts_in_cpu_work = allow;
|
||||||
|
|
||||||
|
if (!advance_pc)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1643,6 +1643,8 @@ void spu_thread::cpu_task()
|
||||||
{
|
{
|
||||||
ensure(spu_runtime::g_interpreter);
|
ensure(spu_runtime::g_interpreter);
|
||||||
|
|
||||||
|
allow_interrupts_in_cpu_work = true;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (state) [[unlikely]]
|
if (state) [[unlikely]]
|
||||||
|
@ -1653,6 +1655,8 @@ void spu_thread::cpu_task()
|
||||||
|
|
||||||
spu_runtime::g_interpreter(*this, _ptr<u8>(0), nullptr);
|
spu_runtime::g_interpreter(*this, _ptr<u8>(0), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allow_interrupts_in_cpu_work = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1663,15 +1667,57 @@ void spu_thread::cpu_work()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u32 old_iter_count = cpu_work_iteration_count++;
|
||||||
|
|
||||||
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
|
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
|
||||||
|
|
||||||
// If either MFC size exceeds limit or timeout has been reached execute pending MFC commands
|
bool work_left = false;
|
||||||
if (mfc_size > g_cfg.core.mfc_transfers_shuffling || (timeout && get_system_time() - mfc_last_timestamp >= timeout))
|
|
||||||
|
if (u32 shuffle_count = g_cfg.core.mfc_transfers_shuffling)
|
||||||
{
|
{
|
||||||
do_mfc(false, false);
|
// If either MFC size exceeds limit or timeout has been reached execute pending MFC commands
|
||||||
|
if (mfc_size > shuffle_count || (timeout && get_system_time() - mfc_last_timestamp >= timeout))
|
||||||
|
{
|
||||||
|
work_left = do_mfc(false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work_left = mfc_size != 0; // TODO: Optimize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool gen_interrupt = false;
|
||||||
|
|
||||||
|
// Check interrupts every 16 iterations
|
||||||
|
if (!(old_iter_count % 16) && allow_interrupts_in_cpu_work)
|
||||||
|
{
|
||||||
|
if (u32 mask = ch_events.load().mask & SPU_EVENT_INTR_BUSY_CHECK)
|
||||||
|
{
|
||||||
|
// LR check is expensive, do it once in a while
|
||||||
|
if (old_iter_count /*% 256*/)
|
||||||
|
{
|
||||||
|
mask &= ~SPU_EVENT_LR;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_events(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_interrupt = check_mfc_interrupts(pc);
|
||||||
|
work_left |= interrupts_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
in_cpu_work = false;
|
in_cpu_work = false;
|
||||||
|
|
||||||
|
if (!work_left)
|
||||||
|
{
|
||||||
|
state -= cpu_flag::pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gen_interrupt)
|
||||||
|
{
|
||||||
|
// Interrupt! escape everything and restart execution
|
||||||
|
spu_runtime::g_escape(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct raw_spu_cleanup
|
struct raw_spu_cleanup
|
||||||
|
@ -2967,7 +3013,7 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args)
|
||||||
vm::reservation_notifier(addr).notify_all(-128);
|
vm::reservation_notifier(addr).notify_all(-128);
|
||||||
}
|
}
|
||||||
|
|
||||||
void spu_thread::do_mfc(bool can_escape, bool must_finish)
|
bool spu_thread::do_mfc(bool can_escape, bool must_finish)
|
||||||
{
|
{
|
||||||
u32 removed = 0;
|
u32 removed = 0;
|
||||||
u32 barrier = 0;
|
u32 barrier = 0;
|
||||||
|
@ -3119,15 +3165,11 @@ void spu_thread::do_mfc(bool can_escape, bool must_finish)
|
||||||
// Exit early, not all pending commands have to be executed at a single iteration
|
// Exit early, not all pending commands have to be executed at a single iteration
|
||||||
// Update last timestamp so the next MFC timeout check will use the current time
|
// Update last timestamp so the next MFC timeout check will use the current time
|
||||||
mfc_last_timestamp = get_system_time();
|
mfc_last_timestamp = get_system_time();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state & cpu_flag::pending)
|
return false;
|
||||||
{
|
|
||||||
// No more pending work
|
|
||||||
state -= cpu_flag::pending;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool spu_thread::check_mfc_interrupts(u32 next_pc)
|
bool spu_thread::check_mfc_interrupts(u32 next_pc)
|
||||||
|
@ -3752,9 +3794,20 @@ void spu_thread::set_interrupt_status(bool enable)
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
// Detect enabling interrupts with events masked
|
// Detect enabling interrupts with events masked
|
||||||
if (auto mask = ch_events.load().mask; mask & ~SPU_EVENT_INTR_IMPLEMENTED)
|
if (auto mask = ch_events.load().mask; mask & SPU_EVENT_INTR_BUSY_CHECK)
|
||||||
{
|
{
|
||||||
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)", mask);
|
if (g_cfg.core.spu_decoder != spu_decoder_type::precise && g_cfg.core.spu_decoder != spu_decoder_type::fast)
|
||||||
|
{
|
||||||
|
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x): Use interpreterts", mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
spu_log.trace("SPU Interrupts (mask=0x%x) are using CPU busy checking mode", mask);
|
||||||
|
|
||||||
|
// Process interrupts in cpu_work()
|
||||||
|
if (state.none_of(cpu_flag::pending))
|
||||||
|
{
|
||||||
|
state += cpu_flag::pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,7 @@ enum : u32
|
||||||
|
|
||||||
SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN | SPU_EVENT_S1 | SPU_EVENT_S2, // Mask of implemented events
|
SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN | SPU_EVENT_S1 | SPU_EVENT_S2, // Mask of implemented events
|
||||||
SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN,
|
SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN,
|
||||||
|
SPU_EVENT_INTR_BUSY_CHECK = SPU_EVENT_IMPLEMENTED & ~SPU_EVENT_INTR_IMPLEMENTED,
|
||||||
SPU_EVENT_INTR_TEST = SPU_EVENT_INTR_IMPLEMENTED,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// SPU Class 0 Interrupts
|
// SPU Class 0 Interrupts
|
||||||
|
@ -779,6 +778,8 @@ public:
|
||||||
static constexpr u32 max_mfc_dump_idx = 2048;
|
static constexpr u32 max_mfc_dump_idx = 2048;
|
||||||
|
|
||||||
bool in_cpu_work = false;
|
bool in_cpu_work = false;
|
||||||
|
bool allow_interrupts_in_cpu_work = false;
|
||||||
|
u8 cpu_work_iteration_count = 0;
|
||||||
|
|
||||||
std::array<v128, 0x4000> stack_mirror; // Return address information
|
std::array<v128, 0x4000> stack_mirror; // Return address information
|
||||||
|
|
||||||
|
@ -793,7 +794,7 @@ public:
|
||||||
bool do_list_transfer(spu_mfc_cmd& args);
|
bool do_list_transfer(spu_mfc_cmd& args);
|
||||||
void do_putlluc(const spu_mfc_cmd& args);
|
void do_putlluc(const spu_mfc_cmd& args);
|
||||||
bool do_putllc(const spu_mfc_cmd& args);
|
bool do_putllc(const spu_mfc_cmd& args);
|
||||||
void do_mfc(bool can_escape = true, bool must_finish = true);
|
bool do_mfc(bool can_escape = true, bool must_finish = true);
|
||||||
u32 get_mfc_completed() const;
|
u32 get_mfc_completed() const;
|
||||||
|
|
||||||
bool process_mfc_cmd();
|
bool process_mfc_cmd();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue