SPU: Implement timer freezing ability

This commit is contained in:
Eladash 2022-05-13 14:50:21 +03:00 committed by Ivan
parent f2920bc30d
commit 2ba437b6dc
4 changed files with 32 additions and 6 deletions

View File

@ -1461,7 +1461,7 @@ void spu_recompiler::RDCH(spu_opcode_t op)
auto sub2 = [](spu_thread* _spu, v128* _res)
{
const u32 out = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp);
const u32 out = _spu->read_dec().first;
*_res = v128::from32r(out);
};
@ -2467,6 +2467,7 @@ void spu_recompiler::WRCH(spu_opcode_t op)
{
auto sub = [](spu_thread* _spu)
{
_spu->get_events(SPU_EVENT_TM);
_spu->ch_dec_start_timestamp = get_timebased_time();
};
@ -2474,6 +2475,7 @@ void spu_recompiler::WRCH(spu_opcode_t op)
c->call(+sub);
c->mov(qw0->r32(), SPU_OFF_32(gpr, op.rt, &v128::_u32, 3));
c->mov(SPU_OFF_32(ch_dec_value), qw0->r32());
c->mov(SPU_OFF_8(is_dec_frozen), 0);
return;
}
case SPU_WrEventMask:

View File

@ -5579,7 +5579,7 @@ public:
static u32 exec_read_dec(spu_thread* _spu)
{
const u32 res = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp);
const u32 res = _spu->read_dec().first;
if (res > 1500 && g_cfg.core.spu_loop_detection)
{
@ -6331,6 +6331,7 @@ public:
call("spu_get_events", &exec_get_events, m_thread, m_ir->getInt32(SPU_EVENT_TM));
m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr<u64>(&spu_thread::ch_dec_start_timestamp));
m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::ch_dec_value));
m_ir->CreateStore(m_ir->getInt8(0), spu_ptr<u8>(&spu_thread::is_dec_frozen));
return;
}
case SPU_Set_Bkmk_Tag:

View File

@ -1309,6 +1309,7 @@ void spu_thread::cpu_init()
ch_dec_start_timestamp = get_timebased_time();
ch_dec_value = option & SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE ? ~static_cast<u32>(ch_dec_start_timestamp) : 0;
is_dec_frozen = false;
if (get_type() >= spu_type::raw)
{
@ -3641,6 +3642,12 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const
return !res;
}
std::pair<u32, u32> spu_thread::read_dec() const
{
const u64 res = ch_dec_value - (is_dec_frozen ? 0 : (get_timebased_time() - ch_dec_start_timestamp));
return {static_cast<u32>(res), static_cast<u32>(res >> 32)};
}
spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool reading)
{
if (auto mask1 = ch_events.load().mask; mask1 & ~SPU_EVENT_IMPLEMENTED)
@ -3664,7 +3671,7 @@ retry:
// SPU Decrementer Event on underflow (use the upper 32-bits to determine it)
if (mask_hint & SPU_EVENT_TM)
{
if (const u64 res = (ch_dec_value - (get_timebased_time() - ch_dec_start_timestamp)) >> 32)
if (const u64 res = read_dec().second)
{
// Set next event to the next time the decrementer underflows
ch_dec_start_timestamp -= res << 32;
@ -3921,7 +3928,7 @@ s64 spu_thread::get_ch_value(u32 ch)
case SPU_RdDec:
{
u32 out = ch_dec_value - static_cast<u32>(get_timebased_time() - ch_dec_start_timestamp);
u32 out = read_dec().first;
//Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down
if (g_cfg.core.spu_loop_detection && out > spu::scheduler::native_jiffy_duration_us)
@ -4337,6 +4344,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
get_events(SPU_EVENT_TM); // Don't discard possibly occured old event
ch_dec_start_timestamp = get_timebased_time();
ch_dec_value = value;
is_dec_frozen = false;
return true;
}
@ -4372,10 +4380,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
// "Collect" events before final acknowledgment
get_events(value);
if (ch_events.atomic_op([&](ch_events_t& events)
bool freeze_dec = false;
const bool check_intr = ch_events.atomic_op([&](ch_events_t& events)
{
events.events &= ~value;
freeze_dec = !!((value & SPU_EVENT_TM) & ~events.mask);
if (events.events & events.mask)
{
events.count = true;
@ -4383,7 +4395,16 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
}
return false;
}))
});
if (!is_dec_frozen && freeze_dec)
{
// Save current time, this will be the reported value until the decrementer resumes
ch_dec_value = read_dec().first;
is_dec_frozen = true;
}
if (check_intr)
{
// Check interrupts in case count is 1
if (check_mfc_interrupts(pc + 4))

View File

@ -733,6 +733,8 @@ public:
u64 ch_dec_start_timestamp; // timestamp of writing decrementer value
u32 ch_dec_value; // written decrementer value
bool is_dec_frozen = false;
std::pair<u32, u32> read_dec() const; // Read decrementer
atomic_t<u32> run_ctrl; // SPU Run Control register (only provided to get latest data written)
shared_mutex run_ctrl_mtx;