From 173fb060cb5dea6c008aabf371e26566eb31a77d Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sat, 24 Jan 2015 00:17:37 +0530 Subject: [PATCH] SPURS: Improve the readability of the event flag functions --- rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp | 180 ++++++++++++++--------- rpcs3/Emu/SysCalls/Modules/cellSpurs.h | 43 +++--- 2 files changed, 134 insertions(+), 89 deletions(-) diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp index 1c15206a46..c8fc774ad8 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp @@ -1696,7 +1696,7 @@ s64 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptrm.direction = flagDirection; eventFlag->m.clearMode = flagClearMode; - eventFlag->m.spuPort = -1; + eventFlag->m.spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT; if (taskset.addr()) { @@ -1734,7 +1734,7 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr eventFlag) 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; } @@ -1755,6 +1755,7 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr eventFlag) auto rc = spursCreateLv2EventQueue(spurs, eventQueueId, port, 1, *((u64 *)"_spuEvF")); 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)); } @@ -1779,6 +1780,8 @@ s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr eventFlag) // { // 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)); } @@ -1811,18 +1814,18 @@ s64 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) 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; } - if (eventFlag->m.x04 || eventFlag->m.x07) + if (eventFlag->m.ppuWaitMask || eventFlag->m.ppuPendingRecv) { return CELL_SPURS_TASK_ERROR_BUSY; } auto port = eventFlag->m.spuPort; - eventFlag->m.spuPort = -1; + eventFlag->m.spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT; vm::ptr spurs; if (eventFlag->m.isIwl == 1) @@ -1851,6 +1854,7 @@ s64 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) 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)); } @@ -1880,83 +1884,104 @@ s64 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr 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; } - if (eventFlag->m.x04 || eventFlag->m.x07) + if (eventFlag->m.ppuWaitMask || eventFlag->m.ppuPendingRecv) { 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) { - u16 tmp = eventFlag->m.x08 & ~eventFlag->m.x02; - if (mode != CELL_SPURS_EVENT_FLAG_AND) + // Make sure the wait mask and mode specified does not conflict with that of the already waiting tasks. + // 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; - while (tmp) + int i = CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS - 1; + 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; } } - tmp >>= 1; + relevantWaitSlots >>= 1; 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; - 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) { - eventFlag->m.bits &= ~bits; + eventFlag->m.events &= ~relevantEvents; } recv = false; } 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) { return CELL_SPURS_TASK_ERROR_BUSY; } - eventFlag->m.x06 = 0; + eventFlag->m.ppuWaitSlotAndMode = 0; if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) { - u8 i = 0; - u16 tmp = eventFlag->m.x08; - while (tmp & 0x01) + // Find an unsed wait slot + int i = 0; + u16 spuTaskUsedWaitSlots = eventFlag->m.spuTaskUsedWaitSlots; + while (spuTaskUsedWaitSlots & 0x0001) { - tmp >>= 1; + spuTaskUsedWaitSlots >>= 1; 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; } - 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; - eventFlag->m.x04 = *mask; - recv = true; + // Save the wait mask and mode for this thread + eventFlag->m.ppuWaitSlotAndMode |= mode; + eventFlag->m.ppuWaitMask = *mask; + recv = true; } + u16 receivedEventFlag; if (recv) { + // Block till something happens vm::var data; auto rc = sys_event_queue_receive(eventFlag->m.eventQueueId, data, 0); if (rc != CELL_OK) @@ -1964,17 +1989,17 @@ s64 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr assert(0); } - u8 i = 0; + int i = 0; if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) { - i = eventFlag->m.x06 >> 4; + i = eventFlag->m.ppuWaitSlotAndMode >> 4; } - bits = eventFlag->m.x30[i]; - eventFlag->m.x07 = 0; + receivedEventFlag = eventFlag->m.pendingRecvTaskEvents[i]; + eventFlag->m.ppuPendingRecv = 0; } - *mask = bits; + *mask = receivedEventFlag; return CELL_OK; } @@ -2006,7 +2031,7 @@ s64 cellSpursEventFlagClear(vm::ptr eventFlag, u16 bits) return CELL_SPURS_TASK_ERROR_ALIGN; } - eventFlag->m.bits &= ~bits; + eventFlag->m.events &= ~bits; return CELL_OK; #endif } @@ -2033,82 +2058,95 @@ s64 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) return CELL_SPURS_TASK_ERROR_PERM; } - u16 tmp1 = 0; - auto send = false; - u16 tmp3 = 0; - u16 tmp4 = 0; - - if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY && eventFlag->m.x04) + u16 ppuEventFlag = 0; + bool send = false; + int ppuWaitSlot = 0; + u16 eventsToClear = 0; + if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY && eventFlag->m.ppuWaitMask) { - u16 tmp = (eventFlag->m.bits | bits) & eventFlag->m.x04; - if ((eventFlag->m.x04 & ~tmp) == 0 || ((eventFlag->m.x06 & 0x0F) == 0 && tmp != 0)) + u16 ppuRelevantEvents = (eventFlag->m.events | bits) & eventFlag->m.ppuWaitMask; + + // 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.x04 = 0; - tmp1 = tmp; - send = true; - tmp3 = eventFlag->m.x06 >> 4; - tmp4 = tmp; + eventFlag->m.ppuPendingRecv = 1; + eventFlag->m.ppuWaitMask = 0; + ppuEventFlag = ppuRelevantEvents; + eventsToClear = ppuRelevantEvents; + ppuWaitSlot = eventFlag->m.ppuWaitSlotAndMode >> 4; + send = true; } } - u16 i = 15; - u16 j = 0; - u16 tmp = eventFlag->m.x08 & ~eventFlag->m.x02; - u16 tmp5 = 0; - u16 x30[16]; - while (tmp) + int i = CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS - 1; + int j = 0; + u16 relevantWaitSlots = eventFlag->m.spuTaskUsedWaitSlots & ~eventFlag->m.spuTaskPendingRecv; + u16 spuTaskPendingRecv = 0; + u16 pendingRecvTaskEvents[16]; + while (relevantWaitSlots) { - if (tmp & 0x0001) + if (relevantWaitSlots & 0x0001) { - u16 tmp6 = (eventFlag->m.bits | bits) & eventFlag->m.x10[i]; - if ((eventFlag->m.x10[i] & ~tmp6) == 0 || (((eventFlag->m.x0A >> j) & 0x01) == 0 && (eventFlag->m.x10[i] & ~tmp6) != 0)) + u16 spuTaskRelevantEvents = (eventFlag->m.events | bits) & eventFlag->m.spuTaskWaitMask[i]; + + // 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; - tmp5 |= 1 << j; - x30[j] = tmp6; + eventsToClear |= spuTaskRelevantEvents; + spuTaskPendingRecv |= 1 << j; + pendingRecvTaskEvents[j] = spuTaskRelevantEvents; } } - tmp >>= 1; + relevantWaitSlots >>= 1; i--; j++; } - eventFlag->m.bits |= bits; - eventFlag->m.x02 |= tmp5; + eventFlag->m.events |= bits; + 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) { - eventFlag->m.bits &= ~tmp4; + eventFlag->m.events &= ~eventsToClear; } 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) { 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 taskset; if (eventFlag->m.isIwl) { - cellSpursLookUpTasksetAddress(vm::ptr::make((u32)eventFlag->m.addr), vm::ptr::make(taskset.addr()), eventFlag->m.x60[i]); + cellSpursLookUpTasksetAddress(vm::ptr::make((u32)eventFlag->m.addr), + vm::ptr::make(taskset.addr()), + eventFlag->m.waitingTaskWklId[i]); } else { taskset.value() = (u32)eventFlag->m.addr; } - auto rc = _cellSpursSendSignal(vm::ptr::make(taskset.addr()), eventFlag->m.x50[i]); + auto rc = _cellSpursSendSignal(vm::ptr::make(taskset.addr()), eventFlag->m.waitingTaskId[i]); if (rc == CELL_SPURS_TASK_ERROR_INVAL || rc == CELL_SPURS_TASK_ERROR_STAT) { return CELL_SPURS_TASK_ERROR_FATAL; diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h index 1946675c54..172bd43b23 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h @@ -222,6 +222,13 @@ enum CellSpursEventFlagDirection 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 SPURSManagerEventFlag; class SPURSManagerTaskset; @@ -553,24 +560,24 @@ struct CellSpursEventFlag // Real data struct _CellSpursEventFlag { - be_t bits; // 0x00 Bit mask of event set bits - be_t x02; // 0x02 Bit mask of SPU thread slots whose conditions have met - be_t x04; // 0x04 Wait mask for PPU thread - u8 x06; // 0x06 Top 4 bits: Bit number for PPU thread. Bottom 4 bits: Wait mode of PPU thread - u8 x07; // 0x07 Set to 1 if the blocked PPU thread's condition has been met - be_t x08; // 0x08 Bit mask of used wait slots - be_t x0A; // 0x0A Bit mask of used wait slots whose wait mode is AND - u8 spuPort; // 0x0C - u8 isIwl; // 0x0D - u8 direction; // 0x0E - u8 clearMode; // 0x0F - be_t x10[16]; // 0x10 Wait mask for SPU threads - be_t x30[16]; // 0x30 Received event flag mask for SPU threads - u8 x50[16]; // 0x50 Task id of waiting SPU threads - u8 x60[16]; // 0x50 Workload Ids of waiting SPU threads - be_t addr; // 0x70 - be_t eventPortId; // 0x78 - be_t eventQueueId; // 0x7C + be_t events; // 0x00 Event bits + be_t 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 ppuWaitMask; // 0x04 Wait mask for blocked 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 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 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 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 isIwl; // 0x0D + u8 direction; // 0x0E + u8 clearMode; // 0x0F + be_t spuTaskWaitMask[16]; // 0x10 Wait mask for blocked SPU tasks + be_t pendingRecvTaskEvents[16]; // 0x30 The value of event flag when the wait condition for the thread/task was met + u8 waitingTaskId[16]; // 0x50 Task id of waiting SPU threads + u8 waitingTaskWklId[16]; // 0x60 Workload id of waiting SPU threads + be_t addr; // 0x70 + be_t eventPortId; // 0x78 + be_t eventQueueId; // 0x7C } m; static_assert(sizeof(_CellSpursEventFlag) == size, "Wrong _CellSpursEventFlag size");