SPURS: Improve the readability of the event flag functions

This commit is contained in:
S Gopal Rajagopal 2015-01-24 00:17:37 +05:30
commit 173fb060cb
2 changed files with 134 additions and 89 deletions

View file

@ -1696,7 +1696,7 @@ s64 _cellSpursEventFlagInitialize(vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTas
memset(eventFlag.get_ptr(), 0, CellSpursEventFlag::size); memset(eventFlag.get_ptr(), 0, CellSpursEventFlag::size);
eventFlag->m.direction = flagDirection; eventFlag->m.direction = flagDirection;
eventFlag->m.clearMode = flagClearMode; eventFlag->m.clearMode = flagClearMode;
eventFlag->m.spuPort = -1; eventFlag->m.spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT;
if (taskset.addr()) if (taskset.addr())
{ {
@ -1734,7 +1734,7 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
return CELL_SPURS_TASK_ERROR_PERM; return CELL_SPURS_TASK_ERROR_PERM;
} }
if (eventFlag->m.spuPort != -1) if (eventFlag->m.spuPort != CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT)
{ {
return CELL_SPURS_TASK_ERROR_STAT; return CELL_SPURS_TASK_ERROR_STAT;
} }
@ -1755,6 +1755,7 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
auto rc = spursCreateLv2EventQueue(spurs, eventQueueId, port, 1, *((u64 *)"_spuEvF")); auto rc = spursCreateLv2EventQueue(spurs, eventQueueId, port, 1, *((u64 *)"_spuEvF"));
if (rc != CELL_OK) if (rc != CELL_OK)
{ {
// Return rc if its an error code from SPURS otherwise convert the error code to a SPURS task error code
return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF));
} }
@ -1779,6 +1780,8 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
// { // {
// sys_event_queue_destroy(eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); // sys_event_queue_destroy(eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
// } // }
// Return rc if its an error code from SPURS otherwise convert the error code to a SPURS task error code
return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF));
} }
@ -1811,18 +1814,18 @@ s64 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
return CELL_SPURS_TASK_ERROR_PERM; return CELL_SPURS_TASK_ERROR_PERM;
} }
if (eventFlag->m.spuPort == -1) if (eventFlag->m.spuPort == CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT)
{ {
return CELL_SPURS_TASK_ERROR_STAT; return CELL_SPURS_TASK_ERROR_STAT;
} }
if (eventFlag->m.x04 || eventFlag->m.x07) if (eventFlag->m.ppuWaitMask || eventFlag->m.ppuPendingRecv)
{ {
return CELL_SPURS_TASK_ERROR_BUSY; return CELL_SPURS_TASK_ERROR_BUSY;
} }
auto port = eventFlag->m.spuPort; auto port = eventFlag->m.spuPort;
eventFlag->m.spuPort = -1; eventFlag->m.spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT;
vm::ptr<CellSpurs> spurs; vm::ptr<CellSpurs> spurs;
if (eventFlag->m.isIwl == 1) if (eventFlag->m.isIwl == 1)
@ -1851,6 +1854,7 @@ s64 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
if (rc != CELL_OK) if (rc != CELL_OK)
{ {
// Return rc if its an error code from SPURS otherwise convert the error code to a SPURS task error code
return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF));
} }
@ -1880,83 +1884,104 @@ s64 _cellSpursEventFlagWait(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16>
return CELL_SPURS_TASK_ERROR_PERM; return CELL_SPURS_TASK_ERROR_PERM;
} }
if (block && eventFlag->m.spuPort == -1) if (block && eventFlag->m.spuPort == CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT)
{ {
return CELL_SPURS_TASK_ERROR_STAT; return CELL_SPURS_TASK_ERROR_STAT;
} }
if (eventFlag->m.x04 || eventFlag->m.x07) if (eventFlag->m.ppuWaitMask || eventFlag->m.ppuPendingRecv)
{ {
return CELL_SPURS_TASK_ERROR_BUSY; return CELL_SPURS_TASK_ERROR_BUSY;
} }
u16 bits = eventFlag->m.bits & *mask; u16 relevantEvents = eventFlag->m.events & *mask;
if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY)
{ {
u16 tmp = eventFlag->m.x08 & ~eventFlag->m.x02; // Make sure the wait mask and mode specified does not conflict with that of the already waiting tasks.
if (mode != CELL_SPURS_EVENT_FLAG_AND) // Conflict scenarios:
// OR vs OR - A conflict never occurs
// OR vs AND - A conflict occurs if the masks for the two tasks overlap
// AND vs AND - A conflict occurs if the masks for the two tasks are not the same
// Determine the set of all already waiting tasks whose wait mode/mask can possibly conflict with the specified wait mode/mask.
// This set is equal to 'set of all tasks waiting' - 'set of all tasks whose wait conditions have been met'.
// If the wait mode is OR, we prune the set of all tasks that are waiting in OR mode from the set since a conflict cannot occur
// with an already waiting task in OR mode.
u16 relevantWaitSlots = eventFlag->m.spuTaskUsedWaitSlots & ~eventFlag->m.spuTaskPendingRecv;
if (mode == CELL_SPURS_EVENT_FLAG_OR)
{ {
tmp &= eventFlag->m.x0A; relevantWaitSlots &= eventFlag->m.spuTaskWaitMode;
} }
int i = 15; int i = CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS - 1;
while (tmp) while (relevantWaitSlots)
{ {
if (tmp & 0x1) if (relevantWaitSlots & 0x0001)
{ {
if (eventFlag->m.x10[i] & *mask && eventFlag->m.x10[i] != *mask) if (eventFlag->m.spuTaskWaitMask[i] & *mask && eventFlag->m.spuTaskWaitMask[i] != *mask)
{ {
return CELL_SPURS_TASK_ERROR_AGAIN; return CELL_SPURS_TASK_ERROR_AGAIN;
} }
} }
tmp >>= 1; relevantWaitSlots >>= 1;
i--; i--;
} }
} }
// There is no need to block if all bits required by the wait operation have already been set or
// if the wait mode is OR and atleast one of the bits required by the wait operation has been set.
bool recv; bool recv;
if ((*mask & ~bits) == 0 || (mode == CELL_SPURS_EVENT_FLAG_OR && bits)) if ((*mask & ~relevantEvents) == 0 || (mode == CELL_SPURS_EVENT_FLAG_OR && relevantEvents))
{ {
// If the clear flag is AUTO then clear the bits comnsumed by this thread
if (eventFlag->m.clearMode == CELL_SPURS_EVENT_FLAG_CLEAR_AUTO) if (eventFlag->m.clearMode == CELL_SPURS_EVENT_FLAG_CLEAR_AUTO)
{ {
eventFlag->m.bits &= ~bits; eventFlag->m.events &= ~relevantEvents;
} }
recv = false; recv = false;
} }
else else
{ {
// If we reach here it means that the conditions for this thread have not been met.
// If this is a try wait operation then do not block but return an error code.
if (block == 0) if (block == 0)
{ {
return CELL_SPURS_TASK_ERROR_BUSY; return CELL_SPURS_TASK_ERROR_BUSY;
} }
eventFlag->m.x06 = 0; eventFlag->m.ppuWaitSlotAndMode = 0;
if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY)
{ {
u8 i = 0; // Find an unsed wait slot
u16 tmp = eventFlag->m.x08; int i = 0;
while (tmp & 0x01) u16 spuTaskUsedWaitSlots = eventFlag->m.spuTaskUsedWaitSlots;
while (spuTaskUsedWaitSlots & 0x0001)
{ {
tmp >>= 1; spuTaskUsedWaitSlots >>= 1;
i++; i++;
} }
if (i == 16) if (i == CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS)
{ {
// Event flag has no empty wait slots
return CELL_SPURS_TASK_ERROR_BUSY; return CELL_SPURS_TASK_ERROR_BUSY;
} }
eventFlag->m.x06 = (15 - i) << 4; // Mark the found wait slot as used by this thread
eventFlag->m.ppuWaitSlotAndMode = (CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS - 1 - i) << 4;
} }
eventFlag->m.x06 |= mode; // Save the wait mask and mode for this thread
eventFlag->m.x04 = *mask; eventFlag->m.ppuWaitSlotAndMode |= mode;
recv = true; eventFlag->m.ppuWaitMask = *mask;
recv = true;
} }
u16 receivedEventFlag;
if (recv) { if (recv) {
// Block till something happens
vm::var<sys_event_data> data; vm::var<sys_event_data> data;
auto rc = sys_event_queue_receive(eventFlag->m.eventQueueId, data, 0); auto rc = sys_event_queue_receive(eventFlag->m.eventQueueId, data, 0);
if (rc != CELL_OK) if (rc != CELL_OK)
@ -1964,17 +1989,17 @@ s64 _cellSpursEventFlagWait(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16>
assert(0); assert(0);
} }
u8 i = 0; int i = 0;
if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY)
{ {
i = eventFlag->m.x06 >> 4; i = eventFlag->m.ppuWaitSlotAndMode >> 4;
} }
bits = eventFlag->m.x30[i]; receivedEventFlag = eventFlag->m.pendingRecvTaskEvents[i];
eventFlag->m.x07 = 0; eventFlag->m.ppuPendingRecv = 0;
} }
*mask = bits; *mask = receivedEventFlag;
return CELL_OK; return CELL_OK;
} }
@ -2006,7 +2031,7 @@ s64 cellSpursEventFlagClear(vm::ptr<CellSpursEventFlag> eventFlag, u16 bits)
return CELL_SPURS_TASK_ERROR_ALIGN; return CELL_SPURS_TASK_ERROR_ALIGN;
} }
eventFlag->m.bits &= ~bits; eventFlag->m.events &= ~bits;
return CELL_OK; return CELL_OK;
#endif #endif
} }
@ -2033,82 +2058,95 @@ s64 cellSpursEventFlagSet(vm::ptr<CellSpursEventFlag> eventFlag, u16 bits)
return CELL_SPURS_TASK_ERROR_PERM; return CELL_SPURS_TASK_ERROR_PERM;
} }
u16 tmp1 = 0; u16 ppuEventFlag = 0;
auto send = false; bool send = false;
u16 tmp3 = 0; int ppuWaitSlot = 0;
u16 tmp4 = 0; u16 eventsToClear = 0;
if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY && eventFlag->m.ppuWaitMask)
if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY && eventFlag->m.x04)
{ {
u16 tmp = (eventFlag->m.bits | bits) & eventFlag->m.x04; u16 ppuRelevantEvents = (eventFlag->m.events | bits) & eventFlag->m.ppuWaitMask;
if ((eventFlag->m.x04 & ~tmp) == 0 || ((eventFlag->m.x06 & 0x0F) == 0 && tmp != 0))
// Unblock the waiting PPU thread if either all the bits being waited by the thread have been set or
// if the wait mode of the thread is OR and atleast one bit the thread is waiting on has been set
if ((eventFlag->m.ppuWaitMask & ~ppuRelevantEvents) == 0 ||
((eventFlag->m.ppuWaitSlotAndMode & 0x0F) == CELL_SPURS_EVENT_FLAG_OR && ppuRelevantEvents != 0))
{ {
eventFlag->m.x07 = 1; eventFlag->m.ppuPendingRecv = 1;
eventFlag->m.x04 = 0; eventFlag->m.ppuWaitMask = 0;
tmp1 = tmp; ppuEventFlag = ppuRelevantEvents;
send = true; eventsToClear = ppuRelevantEvents;
tmp3 = eventFlag->m.x06 >> 4; ppuWaitSlot = eventFlag->m.ppuWaitSlotAndMode >> 4;
tmp4 = tmp; send = true;
} }
} }
u16 i = 15; int i = CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS - 1;
u16 j = 0; int j = 0;
u16 tmp = eventFlag->m.x08 & ~eventFlag->m.x02; u16 relevantWaitSlots = eventFlag->m.spuTaskUsedWaitSlots & ~eventFlag->m.spuTaskPendingRecv;
u16 tmp5 = 0; u16 spuTaskPendingRecv = 0;
u16 x30[16]; u16 pendingRecvTaskEvents[16];
while (tmp) while (relevantWaitSlots)
{ {
if (tmp & 0x0001) if (relevantWaitSlots & 0x0001)
{ {
u16 tmp6 = (eventFlag->m.bits | bits) & eventFlag->m.x10[i]; u16 spuTaskRelevantEvents = (eventFlag->m.events | bits) & eventFlag->m.spuTaskWaitMask[i];
if ((eventFlag->m.x10[i] & ~tmp6) == 0 || (((eventFlag->m.x0A >> j) & 0x01) == 0 && (eventFlag->m.x10[i] & ~tmp6) != 0))
// Unblock the waiting SPU task if either all the bits being waited by the task have been set or
// if the wait mode of the task is OR and atleast one bit the thread is waiting on has been set
if ((eventFlag->m.spuTaskWaitMask[i] & ~spuTaskRelevantEvents) == 0 ||
(((eventFlag->m.spuTaskWaitMode >> j) & 0x0001) == CELL_SPURS_EVENT_FLAG_OR && spuTaskRelevantEvents != 0))
{ {
tmp4 |= tmp6; eventsToClear |= spuTaskRelevantEvents;
tmp5 |= 1 << j; spuTaskPendingRecv |= 1 << j;
x30[j] = tmp6; pendingRecvTaskEvents[j] = spuTaskRelevantEvents;
} }
} }
tmp >>= 1; relevantWaitSlots >>= 1;
i--; i--;
j++; j++;
} }
eventFlag->m.bits |= bits; eventFlag->m.events |= bits;
eventFlag->m.x02 |= tmp5; eventFlag->m.spuTaskPendingRecv |= spuTaskPendingRecv;
// If the clear flag is AUTO then clear the bits comnsumed by all tasks marked to be unblocked
if (eventFlag->m.clearMode == CELL_SPURS_EVENT_FLAG_CLEAR_AUTO) if (eventFlag->m.clearMode == CELL_SPURS_EVENT_FLAG_CLEAR_AUTO)
{ {
eventFlag->m.bits &= ~tmp4; eventFlag->m.events &= ~eventsToClear;
} }
if (send) if (send)
{ {
eventFlag->m.x30[tmp3] = tmp1; // Signal the PPU thread to be woken up
eventFlag->m.pendingRecvTaskEvents[ppuWaitSlot] = ppuEventFlag;
if (sys_event_port_send(eventFlag->m.eventPortId, 0, 0, 0) != CELL_OK) if (sys_event_port_send(eventFlag->m.eventPortId, 0, 0, 0) != CELL_OK)
{ {
assert(0); assert(0);
} }
} }
if (tmp5) if (spuTaskPendingRecv)
{ {
for (auto i = 0; i < 16; i++) // Signal each SPU task whose conditions have been met to be woken up
for (int i = 0; i < CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS; i++)
{ {
if (tmp5 & (0x8000 >> i)) if (spuTaskPendingRecv & (0x8000 >> i))
{ {
eventFlag->m.x30[i] = x30[i]; eventFlag->m.pendingRecvTaskEvents[i] = pendingRecvTaskEvents[i];
vm::var<u32> taskset; vm::var<u32> taskset;
if (eventFlag->m.isIwl) if (eventFlag->m.isIwl)
{ {
cellSpursLookUpTasksetAddress(vm::ptr<CellSpurs>::make((u32)eventFlag->m.addr), vm::ptr<CellSpursTaskset>::make(taskset.addr()), eventFlag->m.x60[i]); cellSpursLookUpTasksetAddress(vm::ptr<CellSpurs>::make((u32)eventFlag->m.addr),
vm::ptr<CellSpursTaskset>::make(taskset.addr()),
eventFlag->m.waitingTaskWklId[i]);
} }
else else
{ {
taskset.value() = (u32)eventFlag->m.addr; taskset.value() = (u32)eventFlag->m.addr;
} }
auto rc = _cellSpursSendSignal(vm::ptr<CellSpursTaskset>::make(taskset.addr()), eventFlag->m.x50[i]); auto rc = _cellSpursSendSignal(vm::ptr<CellSpursTaskset>::make(taskset.addr()), eventFlag->m.waitingTaskId[i]);
if (rc == CELL_SPURS_TASK_ERROR_INVAL || rc == CELL_SPURS_TASK_ERROR_STAT) if (rc == CELL_SPURS_TASK_ERROR_INVAL || rc == CELL_SPURS_TASK_ERROR_STAT)
{ {
return CELL_SPURS_TASK_ERROR_FATAL; return CELL_SPURS_TASK_ERROR_FATAL;

View file

@ -222,6 +222,13 @@ enum CellSpursEventFlagDirection
CELL_SPURS_EVENT_FLAG_LAST = CELL_SPURS_EVENT_FLAG_ANY2ANY, CELL_SPURS_EVENT_FLAG_LAST = CELL_SPURS_EVENT_FLAG_ANY2ANY,
}; };
// Event flag constants
enum SpursEventFlagConstants
{
CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS = 16,
CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT = 0xFF,
};
class SPURSManager; class SPURSManager;
class SPURSManagerEventFlag; class SPURSManagerEventFlag;
class SPURSManagerTaskset; class SPURSManagerTaskset;
@ -553,24 +560,24 @@ struct CellSpursEventFlag
// Real data // Real data
struct _CellSpursEventFlag struct _CellSpursEventFlag
{ {
be_t<u16> bits; // 0x00 Bit mask of event set bits be_t<u16> events; // 0x00 Event bits
be_t<u16> x02; // 0x02 Bit mask of SPU thread slots whose conditions have met be_t<u16> spuTaskPendingRecv; // 0x02 A bit is set to 1 when the condition of the SPU task using the slot are met and back to 0 when the SPU task unblocks
be_t<u16> x04; // 0x04 Wait mask for PPU thread be_t<u16> ppuWaitMask; // 0x04 Wait mask for blocked PPU thread
u8 x06; // 0x06 Top 4 bits: Bit number for PPU thread. Bottom 4 bits: Wait mode of PPU thread u8 ppuWaitSlotAndMode; // 0x06 Top 4 bits: Wait slot number of the blocked PPU threa, Bottom 4 bits: Wait mode of the blocked PPU thread
u8 x07; // 0x07 Set to 1 if the blocked PPU thread's condition has been met u8 ppuPendingRecv; // 0x07 Set to 1 when the blocked PPU thread's conditions are met and back to 0 when the PPU thread is ublocked
be_t<u16> x08; // 0x08 Bit mask of used wait slots be_t<u16> spuTaskUsedWaitSlots; // 0x08 A bit is set to 1 if the wait slot corresponding to the bit is used by an SPU task and 0 otherwise
be_t<u16> x0A; // 0x0A Bit mask of used wait slots whose wait mode is AND be_t<u16> spuTaskWaitMode; // 0x0A A bit is set to 1 if the wait mode for the SPU task corresponding to the bit is AND and 0 otherwise
u8 spuPort; // 0x0C u8 spuPort; // 0x0C
u8 isIwl; // 0x0D u8 isIwl; // 0x0D
u8 direction; // 0x0E u8 direction; // 0x0E
u8 clearMode; // 0x0F u8 clearMode; // 0x0F
be_t<u16> x10[16]; // 0x10 Wait mask for SPU threads be_t<u16> spuTaskWaitMask[16]; // 0x10 Wait mask for blocked SPU tasks
be_t<u16> x30[16]; // 0x30 Received event flag mask for SPU threads be_t<u16> pendingRecvTaskEvents[16]; // 0x30 The value of event flag when the wait condition for the thread/task was met
u8 x50[16]; // 0x50 Task id of waiting SPU threads u8 waitingTaskId[16]; // 0x50 Task id of waiting SPU threads
u8 x60[16]; // 0x50 Workload Ids of waiting SPU threads u8 waitingTaskWklId[16]; // 0x60 Workload id of waiting SPU threads
be_t<u64> addr; // 0x70 be_t<u64> addr; // 0x70
be_t<u32> eventPortId; // 0x78 be_t<u32> eventPortId; // 0x78
be_t<u32> eventQueueId; // 0x7C be_t<u32> eventQueueId; // 0x7C
} m; } m;
static_assert(sizeof(_CellSpursEventFlag) == size, "Wrong _CellSpursEventFlag size"); static_assert(sizeof(_CellSpursEventFlag) == size, "Wrong _CellSpursEventFlag size");