SPU: rewrite spu_channel_t

This commit is contained in:
Nekotekina 2018-04-15 15:37:34 +03:00
parent 3ffafb741c
commit 9ad5fc8a08
2 changed files with 61 additions and 65 deletions

View File

@ -1494,7 +1494,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
{ {
LOG_TRACE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???"); LOG_TRACE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
auto read_channel = [&](spu_channel_t& channel) auto read_channel = [&](spu_channel& channel)
{ {
for (int i = 0; i < 10 && channel.get_count() == 0; i++) for (int i = 0; i < 10 && channel.get_count() == 0; i++)
{ {

View File

@ -153,119 +153,115 @@ enum : u32
RAW_SPU_PROB_OFFSET = 0x00040000, RAW_SPU_PROB_OFFSET = 0x00040000,
}; };
struct spu_channel_t struct spu_channel
{ {
struct alignas(8) sync_var_t // Low 32 bits contain value
{ atomic_t<u64> data;
bool count; // value available
bool wait; // notification required
u32 value;
};
atomic_t<sync_var_t> data;
public: public:
// returns true on success static const u32 off_wait = 32;
static const u32 off_count = 63;
static const u64 bit_wait = 1ull << off_wait;
static const u64 bit_count = 1ull << off_count;
// Returns true on success
bool try_push(u32 value) bool try_push(u32 value)
{ {
const auto old = data.fetch_op([=](sync_var_t& data) const u64 old = data.fetch_op([=](u64& data)
{ {
if ((data.wait = data.count) == false) if (UNLIKELY(data & bit_count))
{ {
data.count = true; data |= bit_wait;
data.value = value; }
else
{
data = bit_count | value;
} }
}); });
return !old.count; return !(old & bit_count);
} }
// push performing bitwise OR with previous value, may require notification // Push performing bitwise OR with previous value, may require notification
void push_or(cpu_thread& spu, u32 value) void push_or(cpu_thread& spu, u32 value)
{ {
const auto old = data.fetch_op([=](sync_var_t& data) const u64 old = data.fetch_op([=](u64& data)
{ {
data.count = true; data &= ~bit_wait;
data.wait = false; data |= bit_count | value;
data.value |= value;
}); });
if (old.wait) spu.notify(); if (old & bit_wait)
{
spu.notify();
}
} }
bool push_and(u32 value) bool push_and(u32 value)
{ {
const auto old = data.fetch_op([=](sync_var_t& data) return (data.fetch_and(~u64{value}) & value) != 0;
{
data.value &= ~value;
});
return (old.value & value) != 0;
} }
// push unconditionally (overwriting previous value), may require notification // Push unconditionally (overwriting previous value), may require notification
void push(cpu_thread& spu, u32 value) void push(cpu_thread& spu, u32 value)
{ {
const auto old = data.fetch_op([=](sync_var_t& data) if (data.exchange(bit_count | value) & bit_wait)
{ {
data.count = true; spu.notify();
data.wait = false; }
data.value = value;
});
if (old.wait) spu.notify();
} }
// returns true on success // Returns true on success
bool try_pop(u32& out) bool try_pop(u32& out)
{ {
const auto old = data.fetch_op([&](sync_var_t& data) const u64 old = data.fetch_op([&](u64& data)
{ {
if (data.count) if (LIKELY(data & bit_count))
{ {
data.wait = false; out = static_cast<u32>(data);
out = data.value; data = 0;
} }
else else
{ {
data.wait = true; data |= bit_wait;
} }
data.count = false;
data.value = 0; // ???
}); });
return old.count; return (old & bit_count) != 0;
} }
// pop unconditionally (loading last value), may require notification // Pop unconditionally (loading last value), may require notification
u32 pop(cpu_thread& spu) u32 pop(cpu_thread& spu)
{ {
const auto old = data.fetch_op([](sync_var_t& data) // Value is not cleared and may be read again
const u64 old = data.fetch_and(~(bit_count | bit_wait));
if (old & bit_wait)
{ {
data.wait = false; spu.notify();
data.count = false; }
// value is not cleared and may be read again
});
if (old.wait) spu.notify(); return static_cast<u32>(old);
return old.value;
} }
void set_value(u32 value, bool count = true) void set_value(u32 value, bool count = true)
{ {
data.store({ count, false, value }); const u64 new_data = u64{count} << off_count | value;
#ifdef _MSC_VER
const_cast<volatile u64&>(data.raw()) = new_data;
#else
__atomic_store_n(&data.raw(), new_data, __ATOMIC_RELAXED);
#endif
} }
u32 get_value() u32 get_value()
{ {
return data.load().value; return static_cast<u32>(data);
} }
u32 get_count() u32 get_count()
{ {
return data.load().count; return static_cast<u32>(data >> off_count);
} }
}; };
@ -551,20 +547,20 @@ public:
u32 srr0; u32 srr0;
u32 ch_tag_upd; u32 ch_tag_upd;
u32 ch_tag_mask; u32 ch_tag_mask;
spu_channel_t ch_tag_stat; spu_channel ch_tag_stat;
u32 ch_stall_mask; u32 ch_stall_mask;
spu_channel_t ch_stall_stat; spu_channel ch_stall_stat;
spu_channel_t ch_atomic_stat; spu_channel ch_atomic_stat;
spu_channel_4_t ch_in_mbox; spu_channel_4_t ch_in_mbox;
spu_channel_t ch_out_mbox; spu_channel ch_out_mbox;
spu_channel_t ch_out_intr_mbox; spu_channel ch_out_intr_mbox;
u64 snr_config; // SPU SNR Config Register u64 snr_config; // SPU SNR Config Register
spu_channel_t ch_snr1; // SPU Signal Notification Register 1 spu_channel ch_snr1; // SPU Signal Notification Register 1
spu_channel_t ch_snr2; // SPU Signal Notification Register 2 spu_channel ch_snr2; // SPU Signal Notification Register 2
atomic_t<u32> ch_event_mask; atomic_t<u32> ch_event_mask;
atomic_t<u32> ch_event_stat; atomic_t<u32> ch_event_stat;