8a0efe548cbed5a5dc6efafe578167cfd1cba6e8
[charm.git] / src / arch / cell / cell_lib / spert_ppu.cpp
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <unistd.h>
6
7 // DEBUG
8 #include <sys/time.h>
9
10 //extern "C" {
11   #include <libspe2.h>
12 //}
13
14 #include "spert_common.h"
15 #include "spert.h"
16
17 extern "C" {
18   #include "spert_ppu.h"
19 }
20
21 #include "pthread.h"
22
23
24 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
25 // Defines
26
27 #define WRHANDLES_NUM_INITIAL  (1024 * 8)
28 #define WRHANDLES_GROW_SIZE    (1024 * 1)
29
30 #define WRGROUPHANDLES_NUM_INITIAL  (1024 * 2)
31 #define WRGROUPHANDLES_GROW_SIZE    (1024 * 1)
32
33 #define MESSAGE_RETURN_CODE_INDEX(rc)  ((unsigned int)(rc) & 0x0000FFFF)
34 #define MESSAGE_RETURN_CODE_ERROR(rc)  (((unsigned int)(rc) >> 16) & 0x0000FFFF)
35
36 #define USE_MESSAGE_QUEUE_FREE_LIST   1  // Set to non-zero to use a linked list of free message queue entries.
37                                          // Note: This can get a free message queue entry in constant time,
38                                          //   i.e. - not f(# SPEs, message queue length), but does not use the
39                                          //   heuristics for load-balancing (at the moment).
40                                          // Note: Currently, the SPE affinity mask is ignored if this is set.
41
42 #define ENABLE_LAST_WR_TIMES          0  // Set to allow a call to displayLastWRTimes() to print the last time
43                                          //   (according to gettimeofday()) that a work request finished for
44                                          //   each SPE.
45
46
47 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
48 // Structures
49
50 typedef struct __pointer_list {
51   void *ptr;
52   struct __pointer_list *next;
53 } PtrList;
54
55 typedef struct __msgQEntry_list {
56   SPEMessage* msgQEntryPtr;
57   int speIndex;
58   int entryIndex;
59   struct __msgQEntry_list *next;
60 } MSGQEntryList;
61
62
63 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
64 // Global Data
65
66
67 /// SDK 2.0 ///
68 //SPEThread* speThreads[NUM_SPE_THREADS];
69
70 /// SDK 2.1 ///
71 SPEThread** speThreads = NULL;
72 int numSPEThreads = -1;
73
74
75 unsigned long long int wrCounter = 0;
76
77 void (*callbackFunc)(void*) = NULL;
78 void (*groupCallbackFunc)(void*) = NULL;
79 void (*errorHandlerFunc)(int, void*, WRHandle) = NULL;
80
81 // Work Request Structures
82 PtrList *allocatedWRHandlesList = NULL;
83
84 // Work Request Group Structures
85 PtrList *allocatedWRGroupHandlesList = NULL;
86
87 // A Queue of WorkRequest structures that have been filled but are waiting on an open SPE messageQueue slot
88 WorkRequest *wrQueuedHead = NULL;
89 WorkRequest *wrQueuedTail = NULL;
90
91 // A Queue of WorkRequest structures that have finished execution on some SPE and are waiting for the
92 //   caller to call isFinished() using the corresponding handle.
93 // NOTE: If InitOffloadAPI() was originally called with a callback function, the callback function will be
94 //   called instead and the WorkRequest structure will be immediately free'd.
95 WorkRequest *wrFinishedHead = NULL;
96 WorkRequest *wrFinishedTail = NULL;
97
98 // A Queue of WorkRequest structures that are free to be used for future work requests.
99 WorkRequest *wrFreeHead = NULL;
100 WorkRequest *wrFreeTail = NULL;
101
102 // A Queue of WRGroup structures that are free to be used for future groups
103 WRGroup *wrGroupFreeHead = NULL;
104 WRGroup *wrGroupFreeTail = NULL;
105
106 // A Queue of free Work Request Entries
107 #if USE_MESSAGE_QUEUE_FREE_LIST != 0
108 MSGQEntryList *msgQEntryFreeHead = NULL;
109 MSGQEntryList *msgQEntryFreeTail = NULL;
110
111 /// SDK 2.0 ///
112 //MSGQEntryList __msgQEntries[NUM_SPE_THREADS * SPE_MESSAGE_QUEUE_LENGTH];
113 //int msgQListLen = NUM_SPE_THREADS * SPE_MESSAGE_QUEUE_LENGTH; // Initially all free
114
115 /// SDK 2.1 ///
116 MSGQEntryList* __msgQEntries;
117 int msgQListLen = -1;
118
119 #endif
120
121 // This is used in an attempt to more evenly distribute the workload amongst all the SPE Threads
122 int speSendStartIndex = 0;
123
124 // A counter used to give each SPE a unique ID
125 unsigned short vIDCounter = 0;
126
127
128 // DEBUG
129 int idCounter = 0;
130
131 // TRACE
132 #if ENABLE_TRACE != 0
133   int traceFlag = 0;
134 #endif
135
136
137 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
138 // "Projections" Data Structures and Function Prototypes
139
140 #if SPE_TIMING != 0
141
142   #define PROJ_BUF_SIZE   (32 * 1024)
143
144   typedef struct __projections_buffer_entry {
145
146     //unsigned long long int startTime;
147     //unsigned int runTime;
148     unsigned int speIndex;
149     unsigned int funcIndex;
150
151     unsigned long long int recvTimeStart;
152     unsigned int recvTimeEnd;
153     unsigned int preFetchingTimeStart;
154     unsigned int preFetchingTimeEnd;
155     unsigned int fetchingTimeStart;
156     unsigned int fetchingTimeEnd;
157     unsigned int readyTimeStart;
158     unsigned int readyTimeEnd;
159     unsigned int userTimeStart;
160     unsigned int userTimeEnd;
161     unsigned int executedTimeStart;
162     unsigned int executedTimeEnd;
163     unsigned int commitTimeStart;    
164     unsigned int commitTimeEnd;
165
166     unsigned int userTime0Start;
167     unsigned int userTime0End;
168     unsigned int userTime1Start;
169     unsigned int userTime1End;
170     unsigned int userTime2Start;
171     unsigned int userTime2End;
172
173     unsigned long long int userAccumTime0;
174     unsigned long long int userAccumTime1;
175     unsigned long long int userAccumTime2;
176     unsigned long long int userAccumTime3;
177
178   } ProjBufEntry;
179
180   // Buffer to hold timing data until the entries are flushed to the file
181   ProjBufEntry projBuf[PROJ_BUF_SIZE];
182   int projBufCount = 0;
183   int totalProjSampleCount = 0;
184
185   FILE* projFile;
186
187   void openProjFile(char* name);
188   void closeProjFile();
189   void addProjEntry(SPENotify* notifyEntry, int speIndex, int funcIndex);
190   void flushProjBuf();
191
192 #endif
193
194
195
196
197 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
198 // Function Prototypes
199
200 SPEThread* createSPEThread(SPEData *speData);
201 SPEThread** createSPEThreads(SPEThread **speThreads, int numThreads);
202
203 int sendSPEMessage(SPEThread* speThread, WorkRequest* wrPtr, int command);
204 //int sendSPEMessage(SPEThread* speThread, int qIndex, WorkRequest* wrPtr, int command);
205 //int sendSPECommand(SPEThread* speThread, int command);
206
207 WorkRequest* createWRHandles(int numHandles);
208 WRGroup* createWRGroupHandles(int numHandles);
209
210
211 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
212 // Offload API Function Bodies
213
214
215 // DEBUG
216 void displayList(char* listName, WorkRequest* list) {
217   printf("PPE :: List \"%s\" = {\n", listName);
218   WorkRequest* entry = list;
219   while (entry != NULL) {
220     printf("PPE ::   %p\n", entry);
221     entry = entry->next;
222   }
223   printf("PPE :: }\n");
224 }
225
226
227 // DEBUG
228 //void displayMessageQueue(int speNum) { displayMessageQueue(speThreads[speNum]); }
229 void displayMessageQueue(SPEThread* speThread) {
230   int speIndex = -1;
231   int i;
232
233   //for (i = 0; i < NUM_SPE_THREADS; i++) if (speThreads[i] == speThread) speIndex = i;
234   for (i = 0; i < numSPEThreads; i++) if (speThreads[i] == speThread) speIndex = i;
235
236   printf("OffloadAPI :: Message Queue for SPE_%d...\n", speIndex);
237   for (i = 0; i < SPE_MESSAGE_QUEUE_LENGTH; i++) {
238     SPEMessage *msg = (SPEMessage*)((char*)(speThread->speData->messageQueue) + (i * SIZEOF_16(SPEMessage)));
239     printf("OffloadAPI ::   [%d]>> state = %d, wrPtr = %p, rw = %lu, ro = %lu, wo = %lu\n", i, msg->state, msg->wrPtr, msg->readWritePtr, msg->readOnlyPtr, msg->writeOnlyPtr);
240   }
241 }
242
243
244 //inline int sendSPEMessage(SPEThread *speThread, int qIndex, WorkRequest *wrPtr, int command) {
245 inline int sendSPEMessage(SPEThread *speThread, int qIndex, WorkRequest *wrPtr, int command, DMAListEntry* dmaListSrc) {
246
247   // Get a pointer to the message queue entry
248   volatile SPEMessage* msg = (SPEMessage*)(((char*)speThread->speData->messageQueue) + (qIndex * SIZEOF_16(SPEMessage)));
249   
250   // Fill in the message queue entry
251   msg->funcIndex = wrPtr->funcIndex;
252   msg->readWritePtr = (PPU_POINTER_TYPE)(wrPtr->readWritePtr);
253   msg->readWriteLen = wrPtr->readWriteLen;
254   msg->readOnlyPtr = (PPU_POINTER_TYPE)(wrPtr->readOnlyPtr);
255   msg->readOnlyLen = wrPtr->readOnlyLen;
256   msg->writeOnlyPtr = (PPU_POINTER_TYPE)(wrPtr->writeOnlyPtr);
257   msg->writeOnlyLen = wrPtr->writeOnlyLen;
258   msg->flags = wrPtr->flags;
259
260   // Copy the DMA list (if is list WR and the dma list is small enought... otherwise, don't bother)
261   if ((wrPtr->flags & WORK_REQUEST_FLAGS_LIST) == WORK_REQUEST_FLAGS_LIST) {
262     register int dmaListSize = wrPtr->readWriteLen + wrPtr->readOnlyLen + wrPtr->writeOnlyLen;
263     if (__builtin_expect(dmaListSize <= SPE_DMA_LIST_LENGTH, 1)) {
264       register volatile DMAListEntry* msgDMAList = msg->dmaList;
265       //register DMAListEntry* wrDMAList = (DMAListEntry*)(wrPtr->readWritePtr);
266       //register DMAListEntry* wrDMAList = (DMAListEntry*)(wrPtr->dmaList);
267       register DMAListEntry* wrDMAList = dmaListSrc;
268
269       // DEBUG
270       //printf(" --- Offload API :: wrPtr = %p, msgDMAList = %p, wrDMAList = %p...\n", wrPtr, msgDMAList, wrDMAList);
271
272       register int i;
273       for (i = 0; i < dmaListSize; i++) {
274         msgDMAList[i].ea = wrDMAList[i].ea;
275         msgDMAList[i].size = ROUNDUP_16(wrDMAList[i].size);
276
277         // DEBUG
278         //printf(" --- Offload API :: msgDMAList[%d] = { ea = 0x%08x, size = %u }\n", i, msgDMAList[i].ea, msgDMAList[i].size);
279       }
280
281       // For the sake of the checksum, clear out the rest of the dma list entries to 0
282       for (; i < SPE_DMA_LIST_LENGTH; i++) {
283         msgDMAList[i].ea = 0;
284         msgDMAList[i].size = 0;
285       }
286
287     }
288   }
289
290   // Calculate the total amount of memory that will be needed on the SPE for this message/work-request
291   #if 0
292     if ((msg->flags & WORK_REQUEST_FLAGS_LIST) == WORK_REQUEST_FLAGS_LIST) {
293       // The memory needed is the size of the DMA list rounded up times 2 (two lists) and the size of each
294       //   of the individual entries in that list all rounded up
295       register int numEntries = wrPtr->readWriteLen + wrPtr->readOnlyLen + wrPtr->writeOnlyLen;
296       msg->totalMem = ROUNDUP_16(sizeof(DMAListEntry) * numEntries);
297       msg->totalMem *= 2;  // Second DMA List within SPE's local store (with LS pointers)
298       for (int entryIndex = 0; entryIndex < numEntries; entryIndex++)
299         msg->totalMem += ROUNDUP_16(((DMAListEntry*)(wrPtr->readWritePtr))[entryIndex].size);
300     } else {
301       // The memory needed is the size of the sum of the three buffers each rounded up
302       msg->totalMem = ROUNDUP_16(wrPtr->readWriteLen) + ROUNDUP_16(wrPtr->readOnlyLen) + ROUNDUP_16(wrPtr->writeOnlyLen);
303     }
304   #else
305     msg->totalMem = 0;
306   #endif
307
308   // TRACE
309   #if ENABLE_TRACE != 0
310     msg->traceFlag = ((__builtin_expect(wrPtr->traceFlag, 0)) ? (-1) : (0));  // force -1 or 0
311   #endif
312
313   msg->state = SPE_MESSAGE_STATE_SENT;
314   msg->command = command;
315   msg->wrPtr = (PPU_POINTER_TYPE)wrPtr;
316
317   // NOTE: Important that the counter be the last then set (the change in this value is what prompts the
318   //   SPE to consider the entry a new entry... even if the state has been set to SENT).
319   // NOTE: Only change the value of msg->counter once so the SPE is not confused (i.e. - don't increment
320   //   and then check msg->counter direclty).
321   int tmp0 = msg->counter0;
322   int tmp1 = msg->counter1;
323   tmp0++; if (__builtin_expect(tmp0 > 0xFFFF, 0)) tmp0 = 0;  // NOTE: Counter value must fit in 16 bits (packed with error code in notify queue)
324   tmp1++; if (__builtin_expect(tmp1 > 0xFFFF, 0)) tmp1 = 0;  // NOTE: Counter value must fit in 16 bits (packed with error code in notify queue)
325   __asm__ ("sync");
326   msg->counter0 = tmp0;
327   msg->counter1 = tmp1;
328   __asm__ ("sync");
329
330
331   // Fill in the check sum
332   register int checkSumVal = 0;
333   register int* intPtr = (int*)msg;
334   register int i;
335   for (i = 0; i < (sizeof(SPEMessage) - sizeof(int)) / sizeof(int); i++) {
336     checkSumVal += intPtr[i];
337   }
338   msg->checksum = checkSumVal;
339
340
341   return qIndex;
342 }
343
344
345 // Returns 0 on success, non-zero otherwise
346 inline int sendSPECommand(SPEThread *speThread, unsigned int command) {
347
348   if (__builtin_expect(command < SPE_MESSAGE_COMMAND_MIN || command > SPE_MESSAGE_COMMAND_MAX, 0)) return -1;
349
350   /// SDK 2.0 ///
351   //
352   //while (spe_stat_in_mbox(speThread->speID) == 0);      // Loop while mailbox is full
353   //return spe_write_in_mbox(speThread->speID, command);
354   //
355
356   /// SDK 2.1 ///
357   return spe_in_mbox_write(speThread->speContext, &command, 1, SPE_MBOX_ALL_BLOCKING);
358 }
359
360
361 extern "C"
362 int InitOffloadAPI(void (*cbFunc)(void*),
363                    void (*gcbFunc)(void*),
364                    void (*errorFunc)(int, void*, WRHandle),
365                    char* timingFileName
366                   ) {
367
368   // Let the user know that the Offload API is being initialized
369   #if DEBUG_DISPLAY >= 1
370     printf("----- Offload API : Enabled... Initializing -----\n");
371   #endif
372
373   // If the caller specified a callback function, set callbackFunc to point to it
374   callbackFunc = ((cbFunc != NULL) ? (cbFunc) : (NULL));
375
376   // If the caller specified a group callback function, set groupCallbackFunc to point to it
377   groupCallbackFunc = ((gcbFunc != NULL) ? (gcbFunc) : (NULL));
378
379   // If the caller specified an error handler function, set errorHandlerFunc to point to it
380   errorHandlerFunc = ((errorFunc != NULL) ? (errorFunc) : (NULL));
381
382
383   /// SDK 2.1 ///
384
385   // Set numSPEThreads
386   #if NUM_SPE_THREADS <= 0
387     // TODO : NOTE : For now, the assumption is made that all CPUs have the same number of SPEs and that
388     //   all will be used (the SDK does not seem to have a function that will identify which CPU this
389     //   process is running on so can't really check how many physical and/or usable SPEs are available
390     //   locally to "this" CPU).  For now, just check how many SPEs CPU 0 has and create that many threads.
391     numSPEThreads = spe_cpu_info_get(SPE_COUNT_PHYSICAL_SPES, 0);
392   #else
393     int numSPEs = spe_cpu_info_get(SPE_COUNT_PHYSICAL_SPES, 0);
394     numSPEThreads = NUM_SPE_THREADS;
395     if (numSPEThreads > numSPEs) {
396       fprintf(stderr, "OffloadAPI :: ERROR : %d SPE(s) were requested but there are only %d physical SPEs\n", numSPEThreads, numSPEs);
397       exit(EXIT_FAILURE);
398     }
399   #endif
400
401   // Create and initialize the speThreads array
402   speThreads = new SPEThread*[numSPEThreads];
403   for (int i = 0; i < numSPEThreads; i++) speThreads[i] = NULL;
404
405   // Create the msgQEntries array (will be initialized later)
406   msgQListLen = numSPEThreads * SPE_MESSAGE_QUEUE_LENGTH;
407   __msgQEntries = new MSGQEntryList[msgQListLen];
408
409
410   // Start Creating the SPE threads
411   #if DEBUG_DISPLAY >= 1
412     printf(" --- Creating SPE Threads ---\n");
413   #endif
414
415   #if CREATE_EACH_THREAD_ONE_BY_ONE
416     //for (int i = 0; i < NUM_SPE_THREADS; i++) {
417     for (int i = 0; i < numSPEThreads; i++) {
418
419       // Create the SPE Thread (with a default SPEData structure)
420       // NOTE: The createSPEThread() call is blocking (it will block until the thread is created).  This
421       //   could be changed in future versions so that the init time will be shortened.
422       speThreads[i] = createSPEThread(NULL);
423       if (speThreads[i] == NULL) {
424         fprintf(stderr, "ERROR : Failed to create SPE Thread %d... Exiting\n", i);
425         exit(EXIT_FAILURE);
426       }
427
428       // Display information about the thread that was just created
429       #if DEBUG_DISPLAY >= 1
430         printf("SPE_%d Created {\n", i);
431         printf("  speThreads[%d]->speData->messageQueue = %p\n", i, (void*)(speThreads[i]->speData->messageQueue));
432         printf("  speThreads[%d]->speData->messageQueueLength = %d\n", i, speThreads[i]->speData->messageQueueLength);
433         printf("  speThreads[%d]->speID = %d\n", i, speThreads[i]->speID);
434         printf("}\n");
435       #endif
436     }
437   #else
438
439     //if (createSPEThreads(speThreads, NUM_SPE_THREADS) == NULL) {
440     if (createSPEThreads(speThreads, numSPEThreads) == NULL) {
441       fprintf(stderr, "OffloadAPI :: ERROR :: createSPEThreads returned NULL... Exiting.\n");
442       exit(EXIT_FAILURE);
443     }
444
445     // Display information about the threads that were just created
446     #if DEBUG_DISPLAY >= 1
447       //for (int i = 0; i < NUM_SPE_THREADS; i++) {
448       for (int i = 0; i < numSPEThreads; i++) {
449         printf("SPE_%d Created {\n", i);
450         printf("  speThreads[%d]->speData->messageQueue = %p\n", i, (void*)(speThreads[i]->speData->messageQueue));
451         printf("  speThreads[%d]->speData->messageQueueLength = %d\n", i, speThreads[i]->speData->messageQueueLength);
452         printf("  speThreads[%d]->speID = %d\n", i, speThreads[i]->speID);
453         printf("}\n");
454       }
455     #endif
456   #endif
457
458   // Add some initial WRHandle structures to the wrFree list
459   wrFreeHead = createWRHandles(WRHANDLES_NUM_INITIAL);
460   wrFreeTail = &(wrFreeHead[WRHANDLES_NUM_INITIAL - 1]);
461
462   // Add some initial WRGroupHandle structures to the wrGroupFree list
463   wrGroupFreeHead = createWRGroupHandles(WRGROUPHANDLES_NUM_INITIAL);
464   wrGroupFreeTail = &(wrGroupFreeHead[WRGROUPHANDLES_NUM_INITIAL - 1]);
465
466   #if USE_MESSAGE_QUEUE_FREE_LIST != 0
467     // Initialize the wrEntryFreeList
468     for (int entryI = 0; entryI < SPE_MESSAGE_QUEUE_LENGTH; entryI++) {
469       //for (int speI = 0; speI < NUM_SPE_THREADS; speI++) {
470       for (int speI = 0; speI < numSPEThreads; speI++) {
471         //register int index = speI + (entryI * NUM_SPE_THREADS);
472         register int index = speI + (entryI * numSPEThreads);
473         __msgQEntries[index].msgQEntryPtr = (SPEMessage*)(((char*)speThreads[speI]->speData->messageQueue) + (entryI * SIZEOF_16(SPEMessage)));
474         __msgQEntries[index].speIndex = speI;
475         __msgQEntries[index].entryIndex = entryI;
476         //__msgQEntries[index].next = ((entryI == (SPE_MESSAGE_QUEUE_LENGTH - 1) && speI == (NUM_SPE_THREADS - 1)) ?
477         __msgQEntries[index].next = ((entryI == (SPE_MESSAGE_QUEUE_LENGTH - 1) && speI == (numSPEThreads - 1)) ?
478                                       (NULL) :
479                                       (&(__msgQEntries[index + 1])));
480       }
481     }
482     msgQEntryFreeHead = &(__msgQEntries[0]);
483     //msgQEntryFreeTail = &(__msgQEntries[(NUM_SPE_THREADS * SPE_MESSAGE_QUEUE_LENGTH) - 1]);
484     msgQEntryFreeTail = &(__msgQEntries[(numSPEThreads * SPE_MESSAGE_QUEUE_LENGTH) - 1]);
485   #endif
486
487   // Open the projections/timing file
488   #if SPE_TIMING != 0
489     openProjFile(timingFileName);
490   #endif
491
492   // Send each of the SPE threads a command to restart their clocks (in an attempt to remove clock
493   //   skew from the timing information).
494   //for (int i = 0; i < NUM_SPE_THREADS; i++)
495   for (int i = 0; i < numSPEThreads; i++)
496     sendSPECommand(speThreads[i], SPE_MESSAGE_COMMAND_RESET_CLOCK);
497
498
499   return 1;
500 }
501
502
503 // STATS
504 #if PPE_STATS != 0
505   long long int iterCount = 0;
506   long long int iterCountCounter = 0;
507   double progress1time = 0.0;
508   double progress2time = 0.0;
509   double progress3time = 0.0;
510   double progress4time = 0.0;
511   int wrInUseCount = 0;
512   int wrFinishedCount = 0;
513   int wrImmedIssueCount = 0;
514 #endif
515
516 extern "C"
517 void CloseOffloadAPI() {
518
519   // STATS
520   #if PPE_STATS != 0
521     printf(" --- Offload API :: [STATS] :: Progress - Iterations = %f, iterCount = %lld, iterCountCounter = %lld\n",
522            (float)iterCount / (float)iterCountCounter, iterCount, iterCountCounter
523           );
524     printf(" --- Offload API :: [STATS] :: Progress - 1:%lf, 2:%lf, 3:%lf, 4:%lf\n",
525            progress1time / (double)iterCountCounter,
526            progress2time / (double)iterCountCounter,
527            progress3time / (double)iterCountCounter,
528            progress4time / (double)iterCountCounter
529           );
530     printf(" --- Offload API :: [STATS] :: Progress - WRs Finished %f - wrFinishedCount = %d\n",
531            (float)wrFinishedCount / (float)iterCountCounter, wrFinishedCount
532           );
533     printf(" --- Offload API :: [STATS] :: Progress - Immed. Issue %f - wrImmedIssueCount = %d\n",
534            (float)wrImmedIssueCount / (float)iterCountCounter, wrImmedIssueCount
535           );
536     printf(" --- Offload API :: [STATS] :: Progress - In Use %f - wrInUseCount = %d\n",
537            (float)wrInUseCount / (float)iterCountCounter, wrInUseCount
538           );
539   #endif
540
541   int status;
542  
543   #if DEBUG_DISPLAY >= 1
544     printf(" ---------- CLOSING OFFLOAD API ----------\n");
545   #endif
546
547   // Send each of the SPE threads a message to exit
548   //for (int i = 0; i < NUM_SPE_THREADS; i++)
549   for (int i = 0; i < numSPEThreads; i++)
550     sendSPECommand(speThreads[i], SPE_MESSAGE_COMMAND_EXIT);
551
552
553   /// SDK 2.0 ///
554   //
555   //// Wait for all the SPE threads to finish
556   //for (int i = 0; i < NUM_SPE_THREADS; i++) {
557   //
558   //  #if DEBUG_DISPLAY >= 1
559   //    printf("OffloadAPI :: Waiting for SPE_%d to Exit...\n", i);
560   //  #endif
561   //
562   //  spe_wait(speThreads[i]->speID, &status, 0);
563   //
564   //  #if DEBUG_DISPLAY >= 1
565   //    printf("OffloadAPI :: SPE_%d Finished (status : %d)\n", i, status);
566   //  #endif
567   //}
568   //
569
570
571   /// SDK 2.1 ///
572
573   // Wait for all the pthreads to complete
574   //for (int i = 0; i < NUM_SPE_THREADS; i++) {
575   for (int i = 0; i < numSPEThreads; i++) {
576
577     int rtnCode = pthread_join(speThreads[i]->pThread, NULL);
578     if (rtnCode != 0) {
579       fprintf(stderr, "OffloadAPI :: ERROR : Unable to join pthread\n");
580       exit(EXIT_FAILURE);
581     }
582   }
583
584
585   // Clean-up any data structures that need cleaned up
586
587   // Clean up the speThreads
588   //for (int i = 0; i < NUM_SPE_THREADS; i++) {
589   for (int i = 0; i < numSPEThreads; i++) {
590     free_aligned((void*)(speThreads[i]->speData->messageQueue));
591     free_aligned((void*)(speThreads[i]->speData));
592     delete speThreads[i];
593   }
594
595   // Clean up the allocated memory for the WRHandles
596   PtrList *entry = allocatedWRHandlesList;
597   while (entry != NULL) {
598     delete [] ((WorkRequest*)(entry->ptr));
599     PtrList *tmp = entry;
600     entry = entry->next;
601     delete tmp;
602   }
603
604   // Close the projections/timing file
605   #if SPE_TIMING != 0
606     closeProjFile();
607   #endif
608
609   #if DEBUG_DISPLAY >= 1
610     printf(" ---------- CLOSING OFFLOAD API ----------\n");
611   #endif
612 }
613
614
615 extern "C"
616 WRHandle sendWorkRequest(int funcIndex,
617                          void* readWritePtr, int readWriteLen,
618                          void* readOnlyPtr, int readOnlyLen,
619                          void* writeOnlyPtr, int writeOnlyLen,
620                          void* userData,
621                          unsigned int flags,
622                          void (*callbackFunc)(void*),
623                          WRGroupHandle wrGroupHandle,
624                          unsigned int speAffinityMask
625                         ) {
626
627   int processingSPEIndex = -1;
628   int sentIndex = -1;
629
630   // Tell the PPU's portion of the spert to make progress
631   //OffloadAPIProgress();
632   //if (msgQListLen == 0)
633   //  OffloadAPIProgress();
634
635   // Verify the parameters
636   #if 1
637     if (__builtin_expect(funcIndex < 0, 0)) return INVALID_WRHandle;
638     if (__builtin_expect((readWritePtr != NULL && readWriteLen <= 0) || (readWriteLen > 0 && readWritePtr == NULL), 0)) return INVALID_WRHandle;
639     if (__builtin_expect((readOnlyPtr  != NULL && readOnlyLen  <= 0) || (readOnlyLen  > 0 && readOnlyPtr  == NULL), 0)) return INVALID_WRHandle;
640     if (__builtin_expect((writeOnlyPtr != NULL && writeOnlyLen <= 0) || (writeOnlyLen > 0 && writeOnlyPtr == NULL), 0)) return INVALID_WRHandle;
641     if (__builtin_expect((flags & WORK_REQUEST_FLAGS_LIST) == WORK_REQUEST_FLAGS_LIST, 0)) {
642       #if DEBUG_DISPLAY >= 1
643         fprintf(stderr, " --- OffloadAPI :: WARNING :: sendWorkRequest() call made with WORK_REQUEST_FLAGS_LIST flag set... ignoring...\n");
644       #endif
645       flags &= (~WORK_REQUEST_FLAGS_LIST);  // Clear the work request's list flag
646     }
647   #endif
648
649   // Ensure that there is at least one free WRHandle structure
650   if (__builtin_expect(wrFreeHead == NULL, 0)) {  // Out of free work request structures
651     // Add some more WRHandle structures to the wrFree list
652     wrFreeHead = createWRHandles(WRHANDLES_GROW_SIZE);
653     wrFreeTail = &(wrFreeHead[WRHANDLES_GROW_SIZE - 1]);
654   }
655
656   // Grab the first free WRHandle structure and use it for this entry
657   WorkRequest *wrEntry = wrFreeHead;
658   wrFreeHead = wrFreeHead->next;
659   if (wrFreeHead == NULL) wrFreeTail = NULL;
660
661   if (__builtin_expect(wrEntry->state != WORK_REQUEST_STATE_FREE, 0)) {
662     fprintf(stderr, " --- Offload API :: ERROR :: Work request struct with state != FREE in free list !!!!!\n");
663   }
664   wrEntry->state = WORK_REQUEST_STATE_INUSE;
665
666   // Fill in the WRHandle structure
667   wrEntry->speAffinityMask = speAffinityMask;
668   wrEntry->speIndex = -1;
669   wrEntry->entryIndex = -1;
670   wrEntry->funcIndex = funcIndex;
671   wrEntry->readWritePtr = readWritePtr;
672   wrEntry->readWriteLen = readWriteLen;
673   wrEntry->readOnlyPtr = readOnlyPtr;
674   wrEntry->readOnlyLen = readOnlyLen;
675   wrEntry->writeOnlyPtr = writeOnlyPtr;
676   wrEntry->writeOnlyLen = writeOnlyLen;
677   wrEntry->userData = userData;
678   wrEntry->callbackFunc = (volatile void (*)(void*))callbackFunc;
679   wrEntry->flags = flags;
680   wrEntry->wrGroupHandle = wrGroupHandle;
681   wrEntry->next = NULL;
682
683
684   // DEBUG
685   wrEntry->id = idCounter;
686   idCounter++;
687
688   // TRACE
689   #if ENABLE_TRACE != 0
690     wrEntry->traceFlag = traceFlag;
691   #endif
692
693   // If this work request is part of a group, increment that group's work request count
694   if (wrGroupHandle != NULL) wrGroupHandle->numWRs++;
695
696   // Try to send the message
697   // NOTE: Through the use of speSendStartIndex, if the SPE's message queues aren't being overloaded, then
698   //   this loop should only iterate once and then break.
699   // TODO : Update this so the number of outstanding work requests that have been sent to each SPE Thread
700   //   is used to pick the SPE Thread to send this request to (i.e. - Send to the thread will the SPE Thread
701   //   with the least full message queue.)  For now, the speSendStartIndex heuristic should help.
702   #if USE_MESSAGE_QUEUE_FREE_LIST == 0
703
704     processingSPEIndex = -1;
705     //for (int i = 0; i < NUM_SPE_THREADS; i++) {
706     for (int i = 0; i < numSPEThreads; i++) {
707
708       // Check the affinity flag (if the bit for this SPE is not set, skip this SPE)
709       if (((0x01 << i) & speAffinityMask) != 0x00) {
710
711         //register int actualSPEThreadIndex = (i + speSendStartIndex) % NUM_SPE_THREADS;
712         register int actualSPEThreadIndex = (i + speSendStartIndex) % numSPEThreads;
713         sentIndex = sendSPEMessage(speThreads[actualSPEThreadIndex], wrEntry, SPE_MESSAGE_COMMAND_NONE);
714
715         if (sentIndex >= 0) {
716           //speSendStartIndex = (actualSPEThreadIndex + 1) % NUM_SPE_THREADS;
717           speSendStartIndex = (actualSPEThreadIndex + 1) % numSPEThreads;
718           processingSPEIndex = actualSPEThreadIndex;
719
720           // Fill in the execution data into the work request (which SPE, which message queue entry)
721           wrEntry->speIndex = processingSPEIndex;
722           wrEntry->entryIndex = sentIndex;
723
724           break;
725         }
726       }
727     }
728
729     // Check to see if the message was sent or not
730     if (processingSPEIndex < 0) {
731
732       // TRACE
733       #if ENABLE_TRACE != 0
734         if (__builtin_expect(wrEntry->traceFlag, 0)) {
735           printf("OffloadAPI :: [TRACE] :: No Slots Open, Queuing Work Request...\n");
736         }
737       #endif
738
739       #if DEBUG_DISPLAY >= 2
740         if (wrQueuedHead != NULL)
741           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
742         else
743           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
744       #endif
745
746       // There were no available spe message queue entries to start the work request so just queue it up in the
747       //   pending list of work requests
748       if (wrQueuedHead == NULL) {
749         wrQueuedHead = wrQueuedTail = wrEntry;  // This entry becomes the list (NOTE: wrEntry->next = NULL)
750       } else {
751         wrQueuedTail->next = wrEntry;  // This entry goes at the end of the current queue (NOTE: wrEntry->next = NULL)
752         wrQueuedTail = wrEntry;
753       }
754
755       #if DEBUG_DISPLAY >= 2
756         if (wrQueuedHead != NULL)
757           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
758         else
759           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
760       #endif
761       #if DEBUG_DISPLAY >= 1
762         printf(" --- Offload API :: Stalled Work Request (SPE Queues FULL: %p) ---\n", wrEntry);
763       #endif
764
765     } else {
766
767       // TRACE
768       #if ENABLE_TRACE != 0
769         if (__builtin_expect(wrEntry->traceFlag, 0)) {
770           printf("OffloadAPI :: [TRACE] :: Work Request Passed to SPE_%d, slot %d... wrEntry = %p...\n", processingSPEIndex, sentIndex, wrEntry);
771           displayMessageQueue(speThreads[processingSPEIndex]);
772         }
773       #endif
774
775       // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
776       wrEntry->speIndex = processingSPEIndex;
777       wrEntry->entryIndex = sentIndex;
778
779       if (__builtin_expect(wrEntry->next != NULL, 0)) {
780         fprintf(stderr, " --- Offload API :: ERROR : Sent work request where wrEntry->next != NULL\n");
781       }
782     }
783
784   #else
785
786     // Check to see of the wrEntryFreeList has an entry
787     //processingSPEIndex = -1;
788     if (msgQEntryFreeHead != NULL) {
789
790       // Remove the entry from the list
791       MSGQEntryList* msgQEntry = msgQEntryFreeHead;
792       msgQEntryFreeHead = msgQEntryFreeHead->next;
793       if (msgQEntryFreeHead == NULL) msgQEntryFreeTail = NULL;
794       msgQEntry->next = NULL;
795       msgQListLen--;
796
797       // Fill in the cross-indexes
798       register int processingSPEIndex = msgQEntry->speIndex;
799       register int sentIndex = msgQEntry->entryIndex;
800
801       // Send the SPE Message
802       sendSPEMessage(speThreads[processingSPEIndex], sentIndex, wrEntry, SPE_MESSAGE_COMMAND_NONE, wrEntry->dmaList);
803
804       // TRACE
805       #if ENABLE_TRACE != 0
806         if (__builtin_expect(wrEntry->traceFlag, 0)) {
807           printf("OffloadAPI :: [TRACE] :: Work Request Passed to SPE_%d, slot %d... wrEntry = %p...\n", processingSPEIndex, sentIndex, wrEntry);
808           displayMessageQueue(speThreads[processingSPEIndex]);
809         }
810       #endif
811
812       // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
813       wrEntry->speIndex = processingSPEIndex;
814       wrEntry->entryIndex = sentIndex;
815
816       if (__builtin_expect(wrEntry->next != NULL, 0)) {
817         fprintf(stderr, " --- Offload API :: ERROR : Sent work request where wrEntry->next != NULL\n");
818       }
819
820     } else {
821
822       // TRACE
823       #if ENABLE_TRACE != 0
824         if (__builtin_expect(wrEntry->traceFlag, 0)) {
825           printf("OffloadAPI :: [TRACE] :: No Slots Open, Queuing Work Request...\n");
826         }
827       #endif
828
829       #if DEBUG_DISPLAY >= 2
830         if (wrQueuedHead != NULL)
831           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
832         else
833           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
834       #endif
835
836       // There were no available spe message queue entries to start the work request so just queue it up in the
837       //   pending list of work requests
838       if (wrQueuedHead == NULL) {
839         wrQueuedHead = wrQueuedTail = wrEntry;  // This entry becomes the list (NOTE: wrEntry->next = NULL)
840       } else {
841         wrQueuedTail->next = wrEntry;  // This entry goes at the end of the current queue (NOTE: wrEntry->next = NULL)
842         wrQueuedTail = wrEntry;
843       }
844
845       #if DEBUG_DISPLAY >= 2
846         if (wrQueuedHead != NULL)
847           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
848         else
849           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
850       #endif
851       #if DEBUG_DISPLAY >= 1
852         printf(" --- Offload API :: Stalled Work Request (SPE Queues FULL: %p) ---\n", wrEntry);
853       #endif
854
855     }
856
857   #endif
858
859   // Return the WorkRequest pointer as the handle
860   return wrEntry;
861 }
862
863
864 extern "C"
865 WRHandle sendWorkRequest_list(int funcIndex,
866                               unsigned int eah,
867                               DMAListEntry* dmaList,
868                               int numReadOnly, int numReadWrite, int numWriteOnly,
869                               void* userData,
870                               unsigned int flags,
871                               void (*callbackFunc)(void*),
872                               WRGroupHandle wrGroupHandle,
873                               unsigned int speAffinityMask
874                              ) {
875
876   int processingSPEIndex = -1;
877   int sentIndex = -1;
878
879   // Tell the PPU's portion of the spert to make progress
880   //OffloadAPIProgress();
881   //if (msgQListLen == 0)
882   //  OffloadAPIProgress();
883
884   // Verify the parameters
885   #if 1
886     if (__builtin_expect(funcIndex < 0, 0)) return INVALID_WRHandle;
887     if (__builtin_expect(dmaList == NULL, 0)) return INVALID_WRHandle;
888     if (__builtin_expect(numReadOnly == 0 && numReadWrite == 0 && numWriteOnly == 0, 0)) return INVALID_WRHandle;
889     if (__builtin_expect(numReadOnly < 0 || numReadWrite < 0 || numWriteOnly < 0, 0)) return INVALID_WRHandle;
890     if (__builtin_expect((flags & (WORK_REQUEST_FLAGS_RW_IS_RO || WORK_REQUEST_FLAGS_RW_IS_WO)) != 0x00, 0)) {
891       #if DEBUG_DISPLAY >= 1
892         fprintf(stderr, "OffloadAPI :: WARNING :: sendWorkRequest_list() call made with WORK_REQUEST_FLAGS_RW_TO_RO and/or WORK_REQUEST_FLAGS_RW_IS_WO flags set... ignoring...\n");
893       #endif
894       flags &= (~(WORK_REQUEST_FLAGS_RW_IS_RO || WORK_REQUEST_FLAGS_RW_IS_WO));  // Force these flags to clear
895     }
896   #endif
897
898   // Ensure that there is at least one free WRHandle structure
899   if (__builtin_expect(wrFreeHead == NULL, 0)) {  // Out of free work request structures
900     // Add some more WRHandle structures to the wrFree list
901     wrFreeHead = createWRHandles(WRHANDLES_GROW_SIZE);
902     wrFreeTail = &(wrFreeHead[WRHANDLES_GROW_SIZE - 1]);
903   }
904
905   // Grab the first free WRHandle structure and use it for this entry
906   WorkRequest *wrEntry = wrFreeHead;
907   wrFreeHead = wrFreeHead->next;
908   if (__builtin_expect(wrFreeHead == NULL, 0)) wrFreeTail = NULL;
909
910   if (__builtin_expect(wrEntry->state != WORK_REQUEST_STATE_FREE, 0)) {
911     fprintf(stderr, " --- Offload API :: ERROR :: Work request struct with state != FREE in free list !!!!!\n");
912   }
913   wrEntry->state = WORK_REQUEST_STATE_INUSE;
914
915   // Fill in the WRHandle structure
916   wrEntry->speAffinityMask = speAffinityMask;
917   wrEntry->speIndex = -1;
918   wrEntry->entryIndex = -1;
919   wrEntry->funcIndex = funcIndex;
920   wrEntry->readWritePtr = dmaList;
921   wrEntry->readWriteLen = numReadWrite;
922   wrEntry->readOnlyPtr = (void*)eah;
923   wrEntry->readOnlyLen = numReadOnly;
924   wrEntry->writeOnlyPtr = NULL;
925   wrEntry->writeOnlyLen = numWriteOnly;
926   wrEntry->userData = userData;
927   wrEntry->callbackFunc = (volatile void (*)(void*))callbackFunc;
928   wrEntry->flags = (flags | WORK_REQUEST_FLAGS_LIST);  // force LIST flag
929   wrEntry->wrGroupHandle = wrGroupHandle;
930   wrEntry->next = NULL;
931
932   // DEBUG
933   wrEntry->id = idCounter;
934   idCounter++;
935
936   // TRACE
937   #if ENABLE_TRACE != 0
938     wrEntry->traceFlag = traceFlag;
939   #endif
940
941   // If this work request is part of a group, increment that group's work request count
942   if (wrGroupHandle != NULL) wrGroupHandle->numWRs++;
943
944   // Try to send the message
945   // NOTE: Through the use of speSendStartIndex, if the SPE's message queues aren't being overloaded, then
946   //   this loop should only iterate once and then break.
947   // TODO : Update this so the number of outstanding work requests that have been sent to each SPE Thread
948   //   is used to pick the SPE Thread to send this request to (i.e. - Send to the thread with the SPE Thread
949   //   with the least full message queue.)  For now, the speSendStartIndex heuristic should help.
950   #if USE_MESSAGE_QUEUE_FREE_LIST == 0
951
952     processingSPEIndex = -1;
953     //for (int i = 0; i < NUM_SPE_THREADS; i++) {
954     for (int i = 0; i < numSPEThreads; i++) {
955
956       // Check the affinity flag (if the bit for this SPE is not set, skip this SPE)
957       if (((0x01 << i) & speAffinityMask) != 0x00) {
958
959         //register int actualSPEThreadIndex = (i + speSendStartIndex) % NUM_SPE_THREADS;
960         register int actualSPEThreadIndex = (i + speSendStartIndex) % numSPEThreads;
961         sentIndex = sendSPEMessage(speThreads[actualSPEThreadIndex], wrEntry, SPE_MESSAGE_COMMAND_NONE);
962
963         if (sentIndex >= 0) {
964           //speSendStartIndex = (actualSPEThreadIndex + 1) % NUM_SPE_THREADS;
965           speSendStartIndex = (actualSPEThreadIndex + 1) % numSPEThreads;
966           processingSPEIndex = actualSPEThreadIndex;
967           break;
968         }
969       }
970     }
971
972     // Check to see if the message was sent or not
973     if (processingSPEIndex < 0) {
974
975       #if DEBUG_DISPLAY >= 2
976         if (wrQueuedHead != NULL)
977           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
978         else
979           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
980       #endif
981
982       // There were no available spe message queue entries to start the work request so just queue it up in the
983       //   pending list of work requests
984       if (wrQueuedHead == NULL) {
985         wrQueuedHead = wrQueuedTail = wrEntry;  // This entry becomes the list (NOTE: wrEntry->next = NULL)
986       } else {
987         wrQueuedTail->next = wrEntry;  // This entry goes at the end of the current queue (NOTE: wrEntry->next = NULL)
988         wrQueuedTail = wrEntry;
989       }
990
991       #if DEBUG_DISPLAY >= 2
992         if (wrQueuedHead != NULL)
993           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
994         else
995           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
996       #endif
997       #if DEBUG_DISPLAY >= 1
998         printf(" --- Offload API :: Stalled Work Request (SPE Queues FULL: %p) ---\n", wrEntry);
999       #endif
1000
1001     } else {
1002
1003       // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
1004       wrEntry->speIndex = processingSPEIndex;
1005       wrEntry->entryIndex = sentIndex;
1006
1007       if (__builtin_expect(wrEntry->next != NULL, 0)) {
1008         fprintf(stderr, " --- Offload API :: ERROR : Sent work request where wrEntry->next != NULL\n");
1009       }
1010     }
1011
1012   #else
1013
1014     // Check to see of the wrEntryFreeList has an entry
1015     processingSPEIndex = -1;
1016     if (msgQEntryFreeHead != NULL) {
1017
1018       // Remove the entry from the list
1019       MSGQEntryList* msgQEntry = msgQEntryFreeHead;
1020       msgQEntryFreeHead = msgQEntryFreeHead->next;
1021       if (msgQEntryFreeHead == NULL) msgQEntryFreeTail = NULL;
1022       msgQEntry->next = NULL;
1023       msgQListLen--;
1024
1025       // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
1026       register int processingSPEIndex = msgQEntry->speIndex;
1027       register int sentIndex = msgQEntry->entryIndex;
1028       wrEntry->speIndex = processingSPEIndex;
1029       wrEntry->entryIndex = sentIndex;
1030       //wrEntry->speIndex = msgQEntry->speIndex;
1031       //wrEntry->entryIndex = msgQEntry->entryIndex;
1032
1033       // DEBUG
1034       if (__builtin_expect(wrEntry->next != NULL, 0)) {
1035         fprintf(stderr, " --- Offload API :: ERROR : Sent work request where wrEntry->next != NULL\n");
1036       }
1037
1038
1039       // TRACE
1040       #if ENABLE_TRACE != 0
1041         if (wrEntry->traceFlag) {
1042
1043           printf("OffloadAPI :: [TRACE] :: (sendWorkRequest_list) processingSPEIndex = %d, sentIndex = %d\n",
1044                  processingSPEIndex, sentIndex
1045                 );
1046
1047           printf("OffloadAPI :: [TRACE] :: (sendWorkRequest_list) dmaList:\n");
1048           register int jj;
1049           for (jj = 0; jj < numReadOnly; jj++) {
1050             printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (RO)\n",
1051                    jj, dmaList[jj].ea, dmaList[jj].size
1052                   );
1053           }
1054           for (; jj < numReadOnly + numReadWrite; jj++) {
1055             printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (RW)\n",
1056                    jj, dmaList[jj].ea, dmaList[jj].size
1057                   );
1058           }
1059           for (; jj < numReadOnly + numReadWrite + numWriteOnly; jj++) {
1060             printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (WO)\n",
1061                    jj, dmaList[jj].ea, dmaList[jj].size
1062                   );
1063           }
1064         }
1065       #endif
1066
1067
1068       // Send the SPE Message (NOTE: The DMA List source is the user's data structure since this Work
1069       //   Request is being issued to the SPE immediately.
1070       sendSPEMessage(speThreads[processingSPEIndex], sentIndex, wrEntry, SPE_MESSAGE_COMMAND_NONE, dmaList);
1071
1072     } else {
1073
1074       #if DEBUG_DISPLAY >= 2
1075         if (wrQueuedHead != NULL)
1076           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1077         else
1078           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1079       #endif
1080
1081       // Copy the DMA list if it should be copied
1082       register int dmaListSize = numReadOnly + numReadWrite + numWriteOnly;
1083       if (__builtin_expect(dmaListSize <= SPE_DMA_LIST_LENGTH, 1)) {
1084         for (int i = 0; i < dmaListSize; i++) {
1085           wrEntry->dmaList[i].ea = dmaList[i].ea;
1086           wrEntry->dmaList[i].size = dmaList[i].size;
1087         }
1088       }
1089
1090       // There were no available spe message queue entries to start the work request so just queue it up in the
1091       //   pending list of work requests
1092       if (wrQueuedHead == NULL) {
1093         wrQueuedHead = wrQueuedTail = wrEntry;  // This entry becomes the list (NOTE: wrEntry->next = NULL)
1094       } else {
1095         wrQueuedTail->next = wrEntry;  // This entry goes at the end of the current queue (NOTE: wrEntry->next = NULL)
1096         wrQueuedTail = wrEntry;
1097       }
1098
1099       #if DEBUG_DISPLAY >= 2
1100         if (wrQueuedHead != NULL)
1101           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1102         else
1103           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1104       #endif
1105       #if DEBUG_DISPLAY >= 1
1106         printf(" --- Offload API :: Stalled Work Request (SPE Queues FULL: %p) ---\n", wrEntry);
1107       #endif
1108
1109     }
1110
1111   #endif
1112
1113   // Return the WorkRequest pointer as the handle
1114   return wrEntry;
1115 }
1116
1117
1118 // Returns: Non-zero if finished, zero otherwise
1119 extern "C"
1120 int isFinished(WRHandle wrHandle) {
1121
1122   int rtnCode = 0;  // default to "not finished"
1123
1124   // Tell the PPE's portion of the spert to make progress
1125   OffloadAPIProgress();
1126
1127   // Check to see if the work request has finished
1128   if (__builtin_expect(wrHandle != INVALID_WRHandle, 1) && wrHandle->state == WORK_REQUEST_STATE_FINISHED) {
1129
1130     // Add this entry to the free list
1131     wrHandle->state = WORK_REQUEST_STATE_FREE;
1132     if (wrFreeTail == NULL) {
1133       wrFreeTail = wrFreeHead = wrHandle;
1134     } else {
1135       wrFreeTail->next = wrHandle;
1136       wrFreeTail = wrHandle;
1137     }
1138
1139     rtnCode = -1;  // change the return code to "finished"
1140   }
1141     
1142   return rtnCode;
1143 }
1144
1145
1146 // TODO : It would be nice to change this from a busy wait to something that busy waits for a
1147 //   short time and then gives up and blocks (Maybe get some OS stuff out of the way while we
1148 //   are just waiting anyway).
1149 // NOTE : This function only blocks if a callbackFunc is not specified.
1150 extern "C"
1151 void waitForWRHandle(WRHandle wrHandle) {
1152
1153   // Verify the WRHandle
1154   if (__builtin_expect(wrHandle == INVALID_WRHandle, 0)) return;
1155
1156   // Check to see if the work request needs to call a callback function of some type (if so, do not block)
1157   if (callbackFunc == NULL && wrHandle != INVALID_WRHandle && wrHandle->callbackFunc == NULL) {
1158     int yeildCounter = 2500;
1159     // Wait for the handle to finish
1160     while (wrHandle != INVALID_WRHandle && !isFinished(wrHandle)) {
1161       yeildCounter--;
1162       if (__builtin_expect(yeildCounter <= 0, 0)) {
1163         // TODO : Place a yield call here (no threading stuff yet)
1164         yeildCounter = 100;
1165       }
1166       // NOTE: isFinished() makes a call to OffloadAPIProgress()... if it is changed so that it
1167       //   doesn't, call OffloadAPIProgress() here.  It MUST be called each iteration of this loop!
1168     }
1169   }
1170 }
1171
1172
1173 extern "C"
1174 WRGroupHandle createWRGroup(void* userData, void (*callbackFunc)(void*)) {
1175
1176   // Ensure that there is at least one free WRGroupHandle structure
1177   if (__builtin_expect(wrGroupFreeHead == NULL, 0)) {
1178     // Add some more WRGroupHandle structures
1179     wrGroupFreeHead = createWRGroupHandles(WRGROUPHANDLES_GROW_SIZE);
1180     wrGroupFreeTail = &(wrGroupFreeHead[WRGROUPHANDLES_GROW_SIZE - 1]);
1181   }
1182
1183   // Grab the first free WRGroupHandle structure and use it for this group
1184   WRGroup* wrGroup = wrGroupFreeHead;
1185   wrGroupFreeHead = wrGroupFreeHead->next;
1186   if (__builtin_expect(wrGroupFreeHead == NULL, 0)) wrGroupFreeTail = NULL;
1187   wrGroup->next = NULL;
1188
1189   // Check and update the WRGroup's state
1190   if (__builtin_expect(wrGroup->state != WRGROUP_STATE_FREE, 0)) {
1191     fprintf(stderr, " --- Offload API :: ERROR :: WRGroup struct with state != FREE in free list !!!!!\n");
1192   }
1193   wrGroup->state = WRGROUP_STATE_FILLING;
1194
1195   // Fill in the WRGroup structure
1196   wrGroup->numWRs = 0;
1197   wrGroup->finishedCount = 0;
1198   wrGroup->userData = userData;
1199   wrGroup->callbackFunc = callbackFunc;
1200
1201   // Return the handle
1202   return wrGroup;
1203 }
1204
1205
1206 extern "C"
1207 void completeWRGroup(WRGroupHandle wrGroupHandle) {
1208
1209   // Verify the parameter
1210   if (__builtin_expect(wrGroupHandle == NULL, 0)) return;
1211
1212   // Check
1213   if (__builtin_expect(wrGroupHandle->state != WRGROUP_STATE_FILLING, 0)) {
1214     fprintf(stderr, " --- Offload API :: ERROR :: WRGroup structure with state != FILLING being completed !!!!!\n");
1215   }
1216
1217   // Update the state of the group
1218   wrGroupHandle->state = WRGROUP_STATE_FULL;
1219
1220   // Check to see if all of the work requests associated with this group have already completed
1221   if (wrGroupHandle->finishedCount >= wrGroupHandle->numWRs) {
1222
1223     // DEBUG
1224     printf(" --- Offload API :: completeGroup() - Immediate complete detected ...\n");
1225
1226     // Get a pointer to the callback function if there is one
1227     //register void (*callbackFunc)(void*);
1228     //if (wrGroupHandle->callbackFunc != NULL)
1229     //  callbackFunc = wrGroupHandle->callbackFunc;
1230     //else
1231     //  callbackFunc = groupCallbackFunc;
1232     register void (*callbackFunc)(void*) = ((wrGroupHandle->callbackFunc != NULL) ?
1233                                               (wrGroupHandle->callbackFunc) :
1234                                               (groupCallbackFunc)
1235                                            );
1236
1237     // Check to see if there is a callback function
1238     if (callbackFunc != NULL) {
1239
1240       // Call the callback function
1241       callbackFunc(wrGroupHandle->userData);
1242
1243       // Clean up the WRGroup structure
1244       wrGroupHandle->state = WRGROUP_STATE_FREE;
1245
1246       // Add the WRGroup structure back into the free list
1247       if (__builtin_expect(wrGroupFreeTail == NULL, 0)) {
1248         wrGroupFreeHead = wrGroupFreeTail = wrGroupHandle;
1249       } else {
1250         wrGroupFreeTail->next = wrGroupHandle;
1251         wrGroupFreeTail = wrGroupHandle;
1252       }
1253
1254     } else {  // Otherwise, there is no callback function
1255
1256       // Mark the group as finished
1257       wrGroupHandle->state = WRGROUP_STATE_FINISHED;
1258     }
1259
1260   } // end if (all work requests in group have finished)
1261 }
1262
1263
1264 // Returns: Non-zero if finished, zero otherwise
1265 extern "C"
1266 int isWRGroupFinished(WRGroupHandle wrGroupHandle) {
1267
1268   int rtnCode = 0;  // Assume "not finished"
1269
1270   // Tell the PPE to make progress
1271   OffloadAPIProgress();
1272
1273   // Check to see if the group has finished
1274   if (__builtin_expect(wrGroupHandle != INVALID_WRGroupHandle, 0) &&
1275       wrGroupHandle->state == WRGROUP_STATE_FINISHED
1276      ) {
1277
1278     // Update the state of the WRGroup structure
1279     wrGroupHandle->state = WRGROUP_STATE_FREE;
1280
1281     // Add this WRGroup structure back into the free list
1282     if (wrGroupFreeTail == NULL) {
1283       wrGroupFreeHead = wrGroupFreeTail = wrGroupHandle;
1284     } else {
1285       wrGroupFreeTail->next = wrGroupHandle;
1286       wrGroupFreeTail = wrGroupHandle;
1287     }
1288
1289     // Set the return code to "finished"
1290     rtnCode = -1;
1291   }
1292
1293   return rtnCode;
1294 }
1295
1296
1297 extern "C"
1298 void waitForWRGroupHandle(WRGroupHandle wrGroupHandle) {
1299
1300   // Verify the parameter
1301   if (__builtin_expect(wrGroupHandle != NULL, 1)) {
1302
1303     // Check to see if a callback function was specified (if so, do not block)
1304     if (groupCallbackFunc == NULL && wrGroupHandle->callbackFunc == NULL) {
1305
1306       // TODO : Add a yeild call into this
1307       while (!isWRGroupFinished(wrGroupHandle));
1308
1309     }
1310   }  
1311 }
1312
1313
1314 // DEBUG
1315 #if ENABLE_LAST_WR_TIMES != 0
1316   timeval lastWRTime[NUM_SPE_THREADS];
1317   void displayLastWRTimes() {
1318     for (int i = 0; i < NUM_SPE_THREADS; i++) {
1319       double timeD = (double)lastWRTime[i].tv_sec + ((double)lastWRTime[i].tv_usec / 1000000.0);
1320       printf("PPE :: displayLastWRTimes() - SPE_%d -> %.9lf sec\n", i, timeD);
1321     }
1322   }
1323 #else
1324   void displayLastWRTimes() { }
1325 #endif
1326
1327
1328 #if 0
1329
1330 // Original
1331
1332 extern "C"
1333 void OffloadAPIProgress() {
1334
1335   // Mailbox Statistics
1336   #define OffloadAPIProgress_statFreq  0
1337   #if OffloadAPIProgress_statFreq > 0
1338     static int statCount = OffloadAPIProgress_statFreq;
1339     int statCount_flag = 0;
1340     static int statSum_all[8] = { 0 };
1341     static int statSum_all_count[8] = { 0 };
1342     static int statSum_nonZero[8] = { 0 };
1343     static int statSum_nonZero_count[8] = { 0 };
1344     static int queueSample[NUM_SPE_THREADS][SPE_MESSAGE_NUM_STATES] = { 0 };
1345     static int queueSample_count = OffloadAPIProgress_statFreq;
1346   #endif
1347
1348
1349   #if OffloadAPIProgress_statFreq > 0
1350     queueSample_count--;
1351     for (int i = 0; i < NUM_SPE_THREADS; i++) {
1352       register char* qStart = (char*)(speThreads[i]->speData->messageQueue);
1353       for (int j = 0; j < SPE_MESSAGE_QUEUE_LENGTH; j++) {
1354         register SPEMessage* qEntry = (SPEMessage*)(qStart + (SIZEOF_16(SPEMessage) * j));
1355         queueSample[i][qEntry->state]++;
1356       }
1357     }
1358
1359     if (queueSample_count <= 0) {
1360       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1361         printf("SPE_%d :: ", i);
1362         for (int j = 0; j < SPE_MESSAGE_NUM_STATES; j++) {
1363           printf("%f ", ((float)(queueSample[i][j]) / (float)(OffloadAPIProgress_statFreq)));
1364           queueSample[i][j] = 0;
1365         }
1366         printf("\n");
1367       }
1368       queueSample_count = OffloadAPIProgress_statFreq;
1369     }
1370   #endif
1371
1372
1373   // Check the mailbox from the SPEs to see if any of the messages have finished (and mark them as such)
1374   for (int i = 0; i < NUM_SPE_THREADS; i++) {
1375
1376     // Get the number of entries in the mailbox from the SPE and then read each entry
1377     int usedEntries = spe_stat_out_mbox(speThreads[i]->speID);
1378
1379     // Mailbox Statistics
1380     #if OffloadAPIProgress_statFreq > 0
1381       statCount_flag += usedEntries;
1382       statSum_all[i] += usedEntries;
1383       statSum_all_count[i]++;
1384       if (usedEntries > 0) {
1385         statSum_nonZero[i] += usedEntries;
1386         statSum_nonZero_count[i]++;
1387       }
1388     #endif
1389
1390     while (usedEntries > 0) {      
1391
1392       // Read the message queue index that was sent by the SPE from the outbound mailbox
1393       unsigned int messageReturnCode = spe_read_out_mbox(speThreads[i]->speID);
1394       unsigned int speMessageQueueIndex = MESSAGE_RETURN_CODE_INDEX(messageReturnCode);
1395       unsigned int speMessageErrorCode = MESSAGE_RETURN_CODE_ERROR(messageReturnCode);
1396       SPEMessage *msg = (SPEMessage*)((char*)(speThreads[i]->speData->messageQueue) + (speMessageQueueIndex * SIZEOF_16(SPEMessage)));
1397
1398       // Get a pointer to the associated work request
1399       WorkRequest *wrPtr = (WorkRequest*)(msg->wrPtr);
1400
1401       if (__builtin_expect(wrPtr == NULL, 0)) {
1402         // Warn the user that something bad has just happened
1403         fprintf(stderr, " --- Offload API :: ERROR :: Received work request completion with no associated work request !!!!!\n");
1404         // Kill self out of shame
1405         exit(EXIT_FAILURE);
1406       }
1407
1408       if (__builtin_expect(wrPtr->next != NULL, 0)) {
1409         // Warn the user that something bad has just happened
1410         fprintf(stderr, " --- Offload API :: ERROR :: WorkRequest finished while still linked (msg->wrPtr->next should be NULL) !!!!!!!\n");
1411         // Kill self out of shame
1412         exit(EXIT_FAILURE);
1413       }
1414
1415       if (__builtin_expect(msg->state != SPE_MESSAGE_STATE_SENT, 0)) {
1416         // Warn the user that something bad has just happened
1417         fprintf(stderr, " --- OffloadAPI :: ERROR :: Invalid message queue index (%d) received from SPE_%d...\n", speMessageQueueIndex, i);
1418         // Kill self out of shame
1419         exit(EXIT_FAILURE);
1420       }
1421
1422       // If there was an error returned by the SPE, display it now
1423       if (__builtin_expect(speMessageErrorCode != SPE_MESSAGE_OK, 0)) {
1424         fprintf(stderr, " --- Offload API :: ERROR :: SPE_%d returned error code %d for message at index %d...\n",
1425                 i, speMessageErrorCode, speMessageQueueIndex
1426                );
1427       }
1428
1429       // DEBUG - Get time last work request completed from this SPE
1430       #if ENABLE_LAST_WR_TIMES != 0
1431         gettimeofday(&(lastWRTime[wrPtr->speIndex]), NULL);
1432       #endif
1433
1434       // TRACE
1435       if (wrPtr->traceFlag) {
1436         printf("OffloadAPI :: [TRACE] :: spe = %d, qi = %d, ec = %d...\n",
1437                i, speMessageQueueIndex, speMessageErrorCode
1438               );
1439         displayMessageQueue(speThreads[i]);
1440       }
1441
1442       // Check to see if this work request should call a callback function upon completion
1443       if (callbackFunc != NULL || wrPtr->callbackFunc != NULL) {
1444
1445         // Call the callback function
1446         if (wrPtr->callbackFunc != NULL)
1447           (wrPtr->callbackFunc)((void*)wrPtr->userData);  // call work request specific callback function
1448         else
1449           callbackFunc((void*)(wrPtr->userData));         // call default work request callback function
1450
1451         // Clear the fields of the work request as needed
1452         //wrPtr->speIndex = -1;
1453         //wrPtr->entryIndex = -1;
1454         //wrPtr->funcIndex = -1;
1455         //wrPtr->readWritePtr = NULL;
1456         //wrPtr->readWriteLen = 0;
1457         //wrPtr->readOnlyPtr = NULL;
1458         //wrPtr->readOnlyLen = 0;
1459         //wrPtr->writeOnlyPtr = NULL;
1460         //wrPtr->writeOnlyLen = 0;
1461         //wrPtr->flags = WORK_REQUEST_FLAGS_NONE;
1462         //wrPtr->userData = NULL;
1463         //wrPtr->callbackFunc = NULL;
1464         //wrPtr->next = NULL;
1465
1466         // Add this entry to the end of the wrFree list
1467         wrPtr->state = WORK_REQUEST_STATE_FREE;
1468         if (wrFreeTail == NULL) {
1469           wrFreeTail = wrFreeHead = wrPtr;
1470         } else {
1471           wrFreeTail->next = wrPtr;
1472           wrFreeTail = wrPtr;
1473         }
1474
1475       // Otherwise, just place the WorkRequest into the wrFinished list
1476       } else {
1477
1478         // Mark the work request as finished
1479         wrPtr->state = WORK_REQUEST_STATE_FINISHED;
1480       }
1481
1482       // Now that the work request has been moved to either the wrFree list or marked as
1483       //   finished, set the state of the message queue entry to clear so it can accempt
1484       //   another work request
1485       msg->state = SPE_MESSAGE_STATE_CLEAR;
1486
1487       // Re-add the message queue entry to the msgQEntryFreeList
1488       #if USE_MESSAGE_QUEUE_FREE_LIST != 0
1489         //MSGQEntryList* msgQEntry = &(__msgQEntries[speMessageQueueIndex + (i * NUM_SPE_THREADS)]);
1490         MSGQEntryList* msgQEntry = &(__msgQEntries[i + (speMessageQueueIndex * NUM_SPE_THREADS)]);
1491         if (__builtin_expect(msgQEntry->next != NULL, 0)) {
1492           printf(" --- OffloadAPI :: ERROR :: msgQEntry->next != NULL !!!!!\n");
1493           msgQEntry->next = NULL;
1494         }
1495         if (msgQEntryFreeTail != NULL) {
1496           msgQEntryFreeTail->next = msgQEntry;
1497           msgQEntryFreeTail = msgQEntry;
1498         } else {
1499           msgQEntryFreeHead = msgQEntryFreeTail = msgQEntry;
1500         }
1501         msgQListLen++;
1502       #endif
1503
1504       // Decrement the count of remaining completion notifications from this SPE
1505       usedEntries--;
1506
1507     } // end while (usedEntries > 0)
1508   } // end for (all SPEs)
1509
1510
1511   // Mailbox Statistics
1512   #if OffloadAPIProgress_statFreq > 0
1513     #if 0
1514       if (statCount_flag > 0)  // For print frequency, only count calls that find at least one mailbox entry
1515         statCount--;
1516       if (statCount <= 0) {
1517         printf("PPE :: OffloadAPIProgress() - Mailbox Statistics...\n");
1518         for (int i = 0; i < NUM_SPE_THREADS; i++) {
1519           printf("PPE :: OffloadAPIProgress() -   SPE_%d Mailbox Stats - all:%.6f(%d), non-zero:%.2f(%d)...\n",
1520                  i,
1521                  ((float)statSum_all[i]) / ((float)statSum_all_count[i]), statSum_all_count[i],
1522                  ((float)statSum_nonZero[i]) / ((float)statSum_nonZero_count[i]), statSum_nonZero_count[i]
1523                 );
1524           statSum_all[i] = 0;
1525           statSum_all_count[i] = 0;
1526           statSum_nonZero[i] = 0;
1527           statSum_nonZero_count[i] = 0;
1528         }
1529         statCount = OffloadAPIProgress_statFreq;
1530       }
1531     #else
1532       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1533         if (statSum_nonZero_count[i] >= OffloadAPIProgress_statFreq) {
1534           printf("PPE :: OffloadAPIProgress() - SPE_%d Mailbox Stats - all:%.6f(%d), non-zero:%.2f(%d)...\n",
1535                  i,
1536                  ((float)statSum_all[i]) / ((float)statSum_all_count[i]), statSum_all_count[i],
1537                  ((float)statSum_nonZero[i]) / ((float)statSum_nonZero_count[i]), statSum_nonZero_count[i]
1538                 );
1539           statSum_all[i] = 0;
1540           statSum_all_count[i] = 0;
1541           statSum_nonZero[i] = 0;
1542           statSum_nonZero_count[i] = 0;
1543         }
1544       }
1545     #endif
1546   #endif
1547
1548
1549   // Loop through the wrQueued list and try to send outstanding messages
1550   int sentIndex = -1;
1551   int processingSPEIndex = -1;
1552   WorkRequest *wrEntry = wrQueuedHead;
1553   WorkRequest *wrEntryPrev = NULL;
1554   while (wrEntry != NULL) {
1555
1556     register unsigned int speAffinityMask = wrEntry->speAffinityMask;
1557
1558     #if USE_MESSAGE_QUEUE_FREE_LIST == 0
1559
1560       // Try each SPE
1561       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1562
1563         // Check the affinity flag (if the bit for this SPE is not set, skip this SPE)
1564         if (((0x01 << i) & speAffinityMask) != 0x00) {
1565           sentIndex = sendSPEMessage(speThreads[i], wrEntry, SPE_MESSAGE_COMMAND_NONE);
1566           if (sentIndex >= 0) {
1567             processingSPEIndex = i;
1568             break;
1569           }
1570         }
1571       }
1572
1573     #else
1574
1575       // Pull the first entry of the message queue free list
1576       if (msgQEntryFreeHead != NULL) {
1577
1578         // Remove the entry from the list
1579         MSGQEntryList* msgQEntry = msgQEntryFreeHead;
1580         msgQEntryFreeHead = msgQEntryFreeHead->next;
1581         if (msgQEntryFreeHead == NULL) msgQEntryFreeTail = NULL;
1582         msgQEntry->next = NULL;
1583         msgQListLen--;
1584
1585         // Fill in the cross-indexes
1586         processingSPEIndex = msgQEntry->speIndex;
1587         sentIndex = msgQEntry->entryIndex;
1588
1589         // Send the SPE Message
1590         sendSPEMessage(speThreads[processingSPEIndex], sentIndex, wrEntry, SPE_MESSAGE_COMMAND_NONE, wrEntr->dmaList);
1591       }
1592
1593     #endif
1594
1595     // Check to see if the message was sent (remove it from the wrQueued list if so)
1596     if (processingSPEIndex >= 0) {
1597
1598       #if DEBUG_DISPLAY >= 1
1599         printf(" --- Offload API :: Stalled Work Request Being Issued (%p) ---\n", wrEntry);
1600         #if DEBUG_DISPLAY >= 2
1601           if (wrQueuedHead != NULL)
1602             printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1603           else
1604             printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1605         #endif
1606       #endif
1607
1608       // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
1609       wrEntry->speIndex = processingSPEIndex;
1610       wrEntry->entryIndex = sentIndex;
1611
1612       // Remove the wrEntry from the wrQueued list
1613       if (wrEntryPrev == NULL) { // is the head of the list
1614         wrQueuedHead = wrEntry->next;
1615         if (wrQueuedHead == NULL) wrQueuedTail = NULL;
1616       } else {  // is in the middle or at the end of the list
1617         wrEntryPrev->next = wrEntry->next;
1618         if (wrEntryPrev->next == NULL) wrQueuedTail = wrEntryPrev;
1619       }
1620       wrEntry->next = NULL;
1621
1622       #if DEBUG_DISPLAY >= 2
1623         if (wrQueuedHead != NULL)
1624           printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1625         else
1626           printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1627       #endif
1628
1629     // Otherwise, there was a work request but no empty slots on any of the SPEs (no need to keep
1630     //   trying more work requests if the work request's affinity specified all SPEs ok)
1631     } else {
1632       if (speAffinityMask == 0xFFFFFFFF) break;
1633     }
1634
1635     // Move into the next wrQueued entry
1636     wrEntryPrev = wrEntry;
1637     wrEntry = wrEntry->next;
1638   }
1639
1640 }
1641
1642 #else
1643
1644 // Experimental
1645
1646 #if SPE_NOTIFY_VIA_MAILBOX != 0
1647
1648 extern "C"
1649 void OffloadAPIProgress() {
1650
1651   // Mailbox Statistics
1652   #define OffloadAPIProgress_statFreq  0
1653   #if OffloadAPIProgress_statFreq > 0
1654     static int statCount = OffloadAPIProgress_statFreq;
1655     int statCount_flag = 0;
1656     static int statSum_all[8] = { 0 };
1657     static int statSum_all_count[8] = { 0 };
1658     static int statSum_nonZero[8] = { 0 };
1659     static int statSum_nonZero_count[8] = { 0 };
1660     static int queueSample[NUM_SPE_THREADS][SPE_MESSAGE_NUM_STATES] = { 0 };
1661     static int queueSample_count = OffloadAPIProgress_statFreq;
1662   #endif
1663
1664
1665   #if OffloadAPIProgress_statFreq > 0
1666     queueSample_count--;
1667     for (int i = 0; i < NUM_SPE_THREADS; i++) {
1668       register char* qStart = (char*)(speThreads[i]->speData->messageQueue);
1669       for (int j = 0; j < SPE_MESSAGE_QUEUE_LENGTH; j++) {
1670         register SPEMessage* qEntry = (SPEMessage*)(qStart + (SIZEOF_16(SPEMessage) * j));
1671         queueSample[i][qEntry->state]++;
1672       }
1673     }
1674
1675     if (queueSample_count <= 0) {
1676       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1677         printf("SPE_%d :: ", i);
1678         for (int j = 0; j < SPE_MESSAGE_NUM_STATES; j++) {
1679           printf("%f ", ((float)(queueSample[i][j]) / (float)(OffloadAPIProgress_statFreq)));
1680           queueSample[i][j] = 0;
1681         }
1682         printf("\n");
1683       }
1684       queueSample_count = OffloadAPIProgress_statFreq;
1685     }
1686   #endif
1687
1688   // STATS
1689   #if PPE_STATS != 0
1690     timeval progress1start;
1691     gettimeofday(&progress1start, NULL);
1692   #endif
1693
1694   // Check the mailbox from the SPEs to see if any of the messages have finished (and mark them as such)
1695   for (int i = 0; i < NUM_SPE_THREADS; i++) {
1696
1697     // STATS
1698     #if PPE_STATS != 0
1699       timeval progress3start;
1700       gettimeofday(&progress3start, NULL);
1701     #endif
1702
1703     // Get the number of entries in the mailbox from the SPE and then read each entry
1704     int usedEntries = spe_stat_out_mbox(speThreads[i]->speID);
1705
1706     // Mailbox Statistics
1707     #if OffloadAPIProgress_statFreq > 0
1708       statCount_flag += usedEntries;
1709       statSum_all[i] += usedEntries;
1710       statSum_all_count[i]++;
1711       if (usedEntries > 0) {
1712         statSum_nonZero[i] += usedEntries;
1713         statSum_nonZero_count[i]++;
1714       }
1715     #endif
1716
1717     // STATS
1718     #if PPE_STATS != 0
1719       timeval progress3end;
1720       gettimeofday(&progress3end, NULL);
1721     #endif
1722
1723     // STATS
1724     #if PPE_STATS != 0
1725       timeval progress4start;
1726       gettimeofday(&progress4start, NULL);
1727     #endif
1728
1729     while (usedEntries > 0) {      
1730
1731       // Read the message queue index that was sent by the SPE from the outbound mailbox
1732       unsigned int messageReturnCode = spe_read_out_mbox(speThreads[i]->speID);
1733       unsigned int speMessageQueueIndex = MESSAGE_RETURN_CODE_INDEX(messageReturnCode);
1734       unsigned int speMessageErrorCode = MESSAGE_RETURN_CODE_ERROR(messageReturnCode);
1735       SPEMessage *msg = (SPEMessage*)((char*)(speThreads[i]->speData->messageQueue) + (speMessageQueueIndex * SIZEOF_16(SPEMessage)));
1736
1737       // Get a pointer to the associated work request
1738       WorkRequest *wrPtr = (WorkRequest*)(msg->wrPtr);
1739
1740       if (__builtin_expect(wrPtr == NULL, 0)) {
1741         // Warn the user that something bad has just happened
1742         fprintf(stderr, " --- Offload API :: ERROR :: Received work request completion with no associated work request !!!!!\n");
1743         // Kill self out of shame
1744         exit(EXIT_FAILURE);
1745       }
1746
1747       if (__builtin_expect(wrPtr->next != NULL, 0)) {
1748         // Warn the user that something bad has just happened
1749         fprintf(stderr, " --- Offload API :: ERROR :: WorkRequest finished while still linked (msg->wrPtr->next should be NULL) !!!!!!!\n");
1750         // Kill self out of shame
1751         exit(EXIT_FAILURE);
1752       }
1753
1754       if (__builtin_expect(msg->state != SPE_MESSAGE_STATE_SENT, 0)) {
1755         // Warn the user that something bad has just happened
1756         fprintf(stderr, " --- OffloadAPI :: ERROR :: Invalid message queue index (%d) received from SPE_%d...\n", speMessageQueueIndex, i);
1757         // Kill self out of shame
1758         exit(EXIT_FAILURE);
1759       }
1760
1761       // If there was an error returned by the SPE, display it now
1762       if (__builtin_expect(speMessageErrorCode != SPE_MESSAGE_OK, 0)) {
1763         fprintf(stderr, " --- Offload API :: ERROR :: SPE_%d returned error code %d for message at index %d...\n",
1764                 i, speMessageErrorCode, speMessageQueueIndex
1765                );
1766       }
1767
1768       // DEBUG - Get time last work request completed from this SPE
1769       #if ENABLE_LAST_WR_TIMES != 0
1770         gettimeofday(&(lastWRTime[wrPtr->speIndex]), NULL);
1771       #endif
1772
1773       // TRACE
1774       #if ENABLE_TRACE != 0
1775         if (__builtin_expect(wrPtr->traceFlag, 0)) {
1776           printf("OffloadAPI :: [TRACE] :: spe = %d, qi = %d, ec = %d...\n",
1777                  i, speMessageQueueIndex, speMessageErrorCode
1778                 );
1779           displayMessageQueue(speThreads[i]);
1780         }
1781       #endif
1782
1783       // Check to see if this work request should call a callback function upon completion
1784       if (callbackFunc != NULL || wrPtr->callbackFunc != NULL) {
1785
1786         // Call the callback function
1787         if (wrPtr->callbackFunc != NULL)
1788           (wrPtr->callbackFunc)((void*)wrPtr->userData);  // call work request specific callback function
1789         else
1790           callbackFunc((void*)(wrPtr->userData));         // call default work request callback function
1791
1792         // Add this entry to the end of the wrFree list
1793         wrPtr->state = WORK_REQUEST_STATE_FREE;
1794         if (wrFreeTail == NULL) {
1795           wrFreeTail = wrFreeHead = wrPtr;
1796         } else {
1797           wrFreeTail->next = wrPtr;
1798           wrFreeTail = wrPtr;
1799         }
1800
1801       // Otherwise, just place the WorkRequest into the wrFinished list
1802       } else {
1803
1804         // Mark the work request as finished
1805         wrPtr->state = WORK_REQUEST_STATE_FINISHED;
1806       }
1807
1808       // Now that the work request has been moved to either the wrFree list or marked as
1809       //   finished, set the state of the message queue entry to clear so it can accempt
1810       //   another work request
1811       msg->state = SPE_MESSAGE_STATE_CLEAR;
1812
1813       // Re-add the message queue entry to the msgQEntryFreeList
1814       #if USE_MESSAGE_QUEUE_FREE_LIST != 0
1815         //MSGQEntryList* msgQEntry = &(__msgQEntries[speMessageQueueIndex + (i * NUM_SPE_THREADS)]);
1816         MSGQEntryList* msgQEntry = &(__msgQEntries[i + (speMessageQueueIndex * NUM_SPE_THREADS)]);
1817         if (__builtin_expect(msgQEntry->next != NULL, 0)) {
1818           printf(" --- OffloadAPI :: ERROR :: msgQEntry->next != NULL !!!!!\n");
1819           msgQEntry->next = NULL;
1820         }
1821         if (msgQEntryFreeTail != NULL) {
1822           msgQEntryFreeTail->next = msgQEntry;
1823           msgQEntryFreeTail = msgQEntry;
1824         } else {
1825           msgQEntryFreeHead = msgQEntryFreeTail = msgQEntry;
1826         }
1827         msgQListLen++;
1828       #endif
1829
1830       // Decrement the count of remaining completion notifications from this SPE
1831       usedEntries--;
1832
1833     } // end while (usedEntries > 0)
1834
1835     // STATS
1836     #if PPE_STATS != 0
1837       timeval progress4end;
1838       gettimeofday(&progress4end, NULL);
1839     #endif
1840
1841     // STATS
1842     #if PPE_STATS != 0
1843       // Calculate the time taken
1844       double startTimeD = (double)progress3start.tv_sec + ((double)progress3start.tv_usec / 1000000.0);
1845       double endTimeD = (double)progress3end.tv_sec + ((double)progress3end.tv_usec / 1000000.0);
1846       double timeDiff = endTimeD - startTimeD;
1847       progress3time += timeDiff;
1848
1849       startTimeD = (double)progress4start.tv_sec + ((double)progress4start.tv_usec / 1000000.0);
1850       endTimeD = (double)progress4end.tv_sec + ((double)progress4end.tv_usec / 1000000.0);
1851       timeDiff = endTimeD - startTimeD;
1852       progress4time += timeDiff;
1853     #endif
1854
1855   } // end for (all SPEs)
1856
1857   // STATS
1858   #if PPE_STATS != 0
1859     timeval progress1end;
1860     gettimeofday(&progress1end, NULL);
1861   #endif
1862
1863   // Mailbox Statistics
1864   #if OffloadAPIProgress_statFreq > 0
1865     #if 0
1866       if (statCount_flag > 0)  // For print frequency, only count calls that find at least one mailbox entry
1867         statCount--;
1868       if (statCount <= 0) {
1869         printf("PPE :: OffloadAPIProgress() - Mailbox Statistics...\n");
1870         for (int i = 0; i < NUM_SPE_THREADS; i++) {
1871           printf("PPE :: OffloadAPIProgress() -   SPE_%d Mailbox Stats - all:%.6f(%d), non-zero:%.2f(%d)...\n",
1872                  i,
1873                  ((float)statSum_all[i]) / ((float)statSum_all_count[i]), statSum_all_count[i],
1874                  ((float)statSum_nonZero[i]) / ((float)statSum_nonZero_count[i]), statSum_nonZero_count[i]
1875                 );
1876           statSum_all[i] = 0;
1877           statSum_all_count[i] = 0;
1878           statSum_nonZero[i] = 0;
1879           statSum_nonZero_count[i] = 0;
1880         }
1881         statCount = OffloadAPIProgress_statFreq;
1882       }
1883     #else
1884       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1885         if (statSum_nonZero_count[i] >= OffloadAPIProgress_statFreq) {
1886           printf("PPE :: OffloadAPIProgress() - SPE_%d Mailbox Stats - all:%.6f(%d), non-zero:%.2f(%d)...\n",
1887                  i,
1888                  ((float)statSum_all[i]) / ((float)statSum_all_count[i]), statSum_all_count[i],
1889                  ((float)statSum_nonZero[i]) / ((float)statSum_nonZero_count[i]), statSum_nonZero_count[i]
1890                 );
1891           statSum_all[i] = 0;
1892           statSum_all_count[i] = 0;
1893           statSum_nonZero[i] = 0;
1894           statSum_nonZero_count[i] = 0;
1895         }
1896       }
1897     #endif
1898   #endif
1899
1900   // STATS
1901   #if PPE_STATS != 0
1902     iterCountCounter++;
1903   #endif
1904
1905   // STATS
1906   #if PPE_STATS != 0
1907     timeval progress2start;
1908     gettimeofday(&progress2start, NULL);
1909   #endif
1910
1911   // Loop through the wrQueued list and try to send outstanding messages
1912   int sentIndex = -1;
1913   int processingSPEIndex = -1;
1914   WorkRequest *wrEntry = wrQueuedHead;
1915   WorkRequest *wrEntryPrev = NULL;
1916   while (wrEntry != NULL) {
1917
1918     // STATS
1919     #if PPE_STATS != 0
1920       iterCount++;
1921     #endif
1922
1923     register unsigned int speAffinityMask = wrEntry->speAffinityMask;
1924
1925     #if USE_MESSAGE_QUEUE_FREE_LIST == 0
1926
1927       // Try each SPE
1928       for (int i = 0; i < NUM_SPE_THREADS; i++) {
1929
1930         // Check the affinity flag (if the bit for this SPE is not set, skip this SPE)
1931         if (((0x01 << i) & speAffinityMask) != 0x00) {
1932           sentIndex = sendSPEMessage(speThreads[i], wrEntry, SPE_MESSAGE_COMMAND_NONE);
1933           if (sentIndex >= 0) {
1934             processingSPEIndex = i;
1935             break;
1936           }
1937         }
1938       }
1939
1940       // Check to see if the message was sent (remove it from the wrQueued list if so)
1941       if (processingSPEIndex >= 0) {
1942
1943         #if DEBUG_DISPLAY >= 1
1944           printf(" --- Offload API :: Stalled Work Request Being Issued (%p) ---\n", wrEntry);
1945           #if DEBUG_DISPLAY >= 2
1946             if (wrQueuedHead != NULL)
1947               printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1948             else
1949               printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1950           #endif
1951         #endif
1952
1953         // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
1954         wrEntry->speIndex = processingSPEIndex;
1955         wrEntry->entryIndex = sentIndex;
1956
1957         // Remove the wrEntry from the wrQueued list
1958         if (wrEntryPrev == NULL) { // is the head of the list
1959           wrQueuedHead = wrEntry->next;
1960           if (wrQueuedHead == NULL) wrQueuedTail = NULL;
1961         } else {  // is in the middle or at the end of the list
1962           wrEntryPrev->next = wrEntry->next;
1963           if (wrEntryPrev->next == NULL) wrQueuedTail = wrEntryPrev;
1964         }
1965         wrEntry->next = NULL;
1966
1967         #if DEBUG_DISPLAY >= 2
1968           if (wrQueuedHead != NULL)
1969             printf(":: wrQueuedHead = %p, wrQueuedTail = %p    wrQueuedHead->next = %p\n", wrQueuedHead, wrQueuedTail, wrQueuedHead->next);
1970           else
1971             printf(":: wrQueuedHead = %p, wrQueuedTail = %p\n", wrQueuedHead, wrQueuedTail);
1972         #endif
1973
1974       // Otherwise, there was a work request but no empty slots on any of the SPEs (no need to keep
1975       //   trying more work requests if the work request's affinity specified all SPEs ok)
1976       } else {
1977         if (speAffinityMask == 0xFFFFFFFF) break;
1978       }
1979
1980     #else
1981
1982       // Pull the first entry of the message queue free list
1983       if (msgQEntryFreeHead != NULL) {
1984
1985         // Remove the entry from the list
1986         MSGQEntryList* msgQEntry = msgQEntryFreeHead;
1987         msgQEntryFreeHead = msgQEntryFreeHead->next;
1988         if (msgQEntryFreeHead == NULL) msgQEntryFreeTail = NULL;
1989         msgQEntry->next = NULL;
1990         msgQListLen--;
1991
1992         // Fill in the cross-indexes
1993         register int processingSPEIndex = msgQEntry->speIndex;
1994         register int sentIndex = msgQEntry->entryIndex;
1995
1996         // Send the SPE Message
1997         sendSPEMessage(speThreads[processingSPEIndex], sentIndex, wrEntry, SPE_MESSAGE_COMMAND_NONE, wrEntry->dmaList);
1998
1999         // Set the speIndex and entryIndex of the work request since it is queued in an SPE's message queue
2000         wrEntry->speIndex = processingSPEIndex;
2001         wrEntry->entryIndex = sentIndex;
2002
2003         // Remove the wrEntry from the wrQueued list
2004         if (wrEntryPrev == NULL) { // is the head of the list
2005           wrQueuedHead = wrEntry->next;
2006           if (wrQueuedHead == NULL) wrQueuedTail = NULL;
2007         } else {  // is in the middle or at the end of the list
2008           wrEntryPrev->next = wrEntry->next;
2009           if (wrEntryPrev->next == NULL) wrQueuedTail = wrEntryPrev;
2010         }
2011         wrEntry->next = NULL;
2012
2013       } else {
2014
2015         break;  // Quit the loop
2016       }
2017
2018     #endif
2019
2020     // Move into the next wrQueued entry
2021     wrEntryPrev = wrEntry;
2022     wrEntry = wrEntry->next;
2023   }
2024
2025   // STATS
2026   #if PPE_STATS != 0
2027     timeval progress2end;
2028     gettimeofday(&progress2end, NULL);
2029   #endif
2030
2031   // STATS
2032   #if PPE_STATS != 0
2033     // Calculate the time taken
2034     double startTimeD = (double)progress1start.tv_sec + ((double)progress1start.tv_usec / 1000000.0);
2035     double endTimeD = (double)progress1end.tv_sec + ((double)progress1end.tv_usec / 1000000.0);
2036     double timeDiff = endTimeD - startTimeD;
2037     progress1time += timeDiff;
2038
2039     // Calculate the time taken
2040     startTimeD = (double)progress2start.tv_sec + ((double)progress2start.tv_usec / 1000000.0);
2041     endTimeD = (double)progress2end.tv_sec + ((double)progress2end.tv_usec / 1000000.0);
2042     timeDiff = endTimeD - startTimeD;
2043     progress2time += timeDiff;
2044   #endif
2045 }
2046
2047 #else
2048
2049 extern "C"
2050 void OffloadAPIProgress() {
2051
2052   // STATS
2053   #if PPE_STATS != 0
2054     timeval progress1start;
2055     gettimeofday(&progress1start, NULL);
2056   #endif
2057
2058   #define PIPELINE_LOADS   1
2059
2060   #if PIPELINE_LOADS != 0
2061     // DEBUG - Pipeline Loads
2062     register SPEData* speData_0 = speThreads[0]->speData;
2063     register char* msgQueueRaw_0 = (char*)(speData_0->messageQueue);
2064     //register int* notifyQueue_0 = (int*)(speData_0->notifyQueue);
2065     register SPENotify* notifyQueue_0 = (SPENotify*)(speData_0->notifyQueue);
2066     register SPEMessage* msg_0 = (SPEMessage*)(msgQueueRaw_0);
2067     register int state_0 = msg_0->state;
2068     register int counter0_0 = msg_0->counter0;
2069     //register int rtnCode_0 = notifyQueue_0[0];
2070     //#if SPE_TIMING != 0
2071     //  register unsigned long long int notify_startTime_0 = notifyQueue_0[0].startTime;
2072     //  register unsigned int notify_runTime_0 = notifyQueue_0[0].runTime;
2073     //#endif
2074     register int notify_errorCode_0 = notifyQueue_0[0].errorCode;
2075     register int notify_counter_0 = notifyQueue_0[0].counter;
2076   #endif
2077
2078   // Check each message queue entry
2079   for (int j = 0; j < SPE_MESSAGE_QUEUE_LENGTH; j++) {
2080
2081     // For each SPE
2082     //for (int i = 0; i < NUM_SPE_THREADS; i++) {
2083     for (int i = 0; i < numSPEThreads; i++) {
2084
2085       #if PIPELINE_LOADS != 0
2086
2087         // Load this iteration's data
2088         register SPEData* speData = speData_0;
2089         register char* msgQueueRaw = msgQueueRaw_0;
2090         //register int* notifyQueue = notifyQueue_0;
2091         register SPENotify* notifyQueue = notifyQueue_0;
2092         register SPEMessage* msg = msg_0;
2093         register int state = state_0;
2094         register int counter0 = counter0_0;
2095         //register int rtnCode = rtnCode_0;
2096         //#if SPE_TIMING != 0
2097         //  register unsigned long long int notify_startTime = notify_startTime_0;
2098         //  register unsigned int notify_runTime = notify_runTime_0;
2099         //#endif
2100         register int notify_errorCode = notify_errorCode_0;
2101         register int notify_counter = notify_counter_0;
2102
2103         register int i_0 = i + 1;
2104         register int j_0 = j;
2105         //if (__builtin_expect(i_0 >= NUM_SPE_THREADS, 0)) {
2106         if (__builtin_expect(i_0 >= numSPEThreads, 0)) {
2107           j_0++;
2108           i_0 = 0;
2109         }
2110         if (__builtin_expect(j_0 >= SPE_MESSAGE_QUEUE_LENGTH, 0)) {
2111           j_0 = 0;
2112         }
2113
2114         // Next Iteration
2115         speData_0 = speThreads[i_0]->speData;
2116         msgQueueRaw_0 = (char*)(speData_0->messageQueue);
2117         //notifyQueue_0 = (int*)(speData_0->notifyQueue);
2118         notifyQueue_0 = (SPENotify*)(speData_0->notifyQueue);
2119         msg_0 = (SPEMessage*)(msgQueueRaw_0 + (j_0 * SIZEOF_16(SPEMessage)));
2120         state_0 = msg_0->state;
2121         counter0_0 = msg_0->counter0;
2122         //rtnCode_0 = notifyQueue_0[j_0];
2123         //#if SPE_TIMING != 0
2124         //  notify_startTime_0 = notifyQueue_0[j_0].startTime;
2125         //  notify_runTime_0 = notifyQueue_0[j_0].runTime;
2126         //#endif
2127         notify_errorCode_0 = notifyQueue_0[j_0].errorCode;
2128         notify_counter_0 = notifyQueue_0[j_0].counter;
2129
2130       #else
2131
2132         register SPEData* speData = speThreads[i]->speData;
2133         register char* msgQueueRaw = (char*)(speData->messageQueue);
2134         //register int* notifyQueue = (int*)(speData->notifyQueue);
2135         register SPENotify* notifyQueue = (SPENotify*)(speData->notifyQueue);
2136
2137         register SPEMessage* msg = (SPEMessage*)(msgQueueRaw + (j * SIZEOF_16(SPEMessage)));
2138         register int state = msg->state;
2139         register int counter0 = msg->counter0;
2140         //register int rtnCode = notifyQueue[j];
2141         //#if SPE_TIMING != 0
2142         //  register int notify_startTime = notifyQueue[j].startTime;
2143         //  register int notify_runTime = notifyQueue[j].runTime;
2144         //#endif
2145         register int notify_errorCode = notifyQueue[j].errorCode;
2146         register int notify_counter = notifyQueue[j].counter;
2147
2148       #endif
2149
2150       // STATS
2151       #if PPE_STATS != 0
2152         if (state != SPE_MESSAGE_STATE_CLEAR)
2153           wrInUseCount++;
2154       #endif
2155
2156       // Check to see if this message queue entry is pending a completion notification
2157       //if ((state == SPE_MESSAGE_STATE_SENT) && ((rtnCode & 0xFFFF) == counter0)) {
2158       if ((state == SPE_MESSAGE_STATE_SENT) && (notify_counter == counter0)) {
2159
2160         #if SPE_TIMING != 0
2161
2162           //// DEBUG
2163           //printf(" --- Offload API :: [DEBUG] :: WR finished with startTime = %llu, runTime = %u\n",
2164           //       notify_startTime, notify_runTime
2165           //      );
2166
2167           addProjEntry(&(notifyQueue[j]), i, msg->funcIndex);
2168
2169         #endif
2170
2171         // STATS
2172         #if PPE_STATS != 0
2173           wrFinishedCount++;
2174         #endif
2175
2176         // Get a pointer to the associated work request
2177         register WorkRequest* wrPtr = (WorkRequest*)(msg->wrPtr);
2178
2179         // Get the error code
2180         //register int errorCode = (rtnCode >> 16) & 0xFFFF;
2181
2182         //// If there was an error returned by the SPE, display it now
2183         //if (__builtin_expect(errorCode != SPE_MESSAGE_OK, 0)) {
2184         //  fprintf(stderr, " --- Offload API :: ERROR :: SPE_%d returned error code %d for message at index %d...\n",
2185         //          i, errorCode, j
2186         //         );
2187         //}
2188
2189         // TRACE
2190         #if ENABLE_TRACE != 0
2191           if (__builtin_expect(wrPtr->traceFlag, 0)) {
2192             //printf("OffloadAPI :: [TRACE] :: rtnCode = 0x%08x, errorCode(%d,%d) = %d...\n",
2193             //       rtnCode, i, j, errorCode
2194             //      );
2195             printf("OffloadAPI :: [TRACE] :: counter(%d,%d) = %d, errorCode(%d,%d) = %d...\n",
2196                    i, j, notify_counter, i, j, notify_errorCode
2197                   );
2198           }
2199         #endif
2200
2201         // If there was an error returned by the SPE, call the error handler function now
2202         //if (__builtin_expect(notify_errorCode != SPE_MESSAGE_OK, 0)) {
2203         if (__builtin_expect(notify_errorCode != SPE_MESSAGE_OK, 0)) {
2204           if (errorHandlerFunc != NULL) {
2205             //errorHandlerFunc(errorCode, wrPtr->userData, wrPtr);
2206             errorHandlerFunc(notify_errorCode, wrPtr->userData, wrPtr);
2207           } else {
2208             fprintf(stderr, " --- Offload API :: ERROR :: SPE_%d returned error code %d for message at index %d...\n",
2209                     //i, errorCode, j
2210                     i, notify_errorCode, j
2211                    );
2212           }
2213         }
2214
2215         //if (__builtin_expect(wrPtr == NULL, 0)) {
2216         //  // Warn the user that something bad has just happened
2217         //  fprintf(stderr, " --- Offload API :: ERROR :: Received work request completion with no associated work request !!!!!\n");
2218         //  // Kill self out of shame
2219         //  exit(EXIT_FAILURE);
2220         //}
2221
2222         //if (__builtin_expect(wrPtr->next != NULL, 0)) {
2223         //  // Warn the user that something bad has just happened
2224         //  fprintf(stderr, " --- Offload API :: ERROR :: WorkRequest finished while still linked (msg->wrPtr->next should be NULL) !!!!!!!\n");
2225         //  // Kill self out of shame
2226         //  exit(EXIT_FAILURE);
2227         //}
2228
2229         // DEBUG - Get time last work request completed from this SPE
2230         #if ENABLE_LAST_WR_TIMES != 0
2231           gettimeofday(&(lastWRTime[wrPtr->speIndex]), NULL);
2232         #endif
2233
2234         // TRACE
2235         #if ENABLE_TRACE != 0
2236           if (__builtin_expect(wrPtr->traceFlag, 0)) {
2237             printf("OffloadAPI :: [TRACE] :: spe = %d, qi = %d, ec = %d...\n",
2238                    i, j, notify_errorCode
2239                   );
2240             displayMessageQueue(speThreads[i]);
2241           }
2242         #endif
2243
2244         // Check to see if this work request is part of a group
2245         register WRGroup* wrGroup = wrPtr->wrGroupHandle;
2246         if (wrGroup == INVALID_WRGroupHandle) {
2247
2248           // Check to see if this work request should call a callback function upon completion
2249           if (callbackFunc != NULL || wrPtr->callbackFunc != NULL) {
2250
2251             // Call the callback function
2252             if (wrPtr->callbackFunc != NULL)
2253               (wrPtr->callbackFunc)((void*)wrPtr->userData);  // call work request specific callback function
2254             else
2255               callbackFunc((void*)(wrPtr->userData));         // call default work request callback function
2256
2257             // Add this entry to the end of the wrFree list
2258             wrPtr->state = WORK_REQUEST_STATE_FREE;
2259             if (wrFreeTail == NULL) {
2260               wrFreeTail = wrFreeHead = wrPtr;
2261             } else {
2262               wrFreeTail->next = wrPtr;
2263               wrFreeTail = wrPtr;
2264             }
2265
2266           // Otherwise, just place the WorkRequest into the wrFinished list
2267           } else {
2268
2269             // Mark the work request as finished
2270             wrPtr->state = WORK_REQUEST_STATE_FINISHED;
2271           }
2272
2273         } else {  // Otherwise, this work request is part of a group
2274
2275           // Increment the group's finished counter
2276           wrGroup->finishedCount++;
2277
2278           // Check to see if the individual work request's callback should be called in addition to the group's callback.
2279           if ((wrPtr->flags & WORK_REQUEST_FLAGS_BOTH_CALLBACKS) == WORK_REQUEST_FLAGS_BOTH_CALLBACKS) {            
2280             register void (*cbf)(void*) = ((wrPtr->callbackFunc != NULL) ? ((void (*)(void*))wrPtr->callbackFunc) : (callbackFunc));
2281             if (cbf != NULL) cbf(wrPtr->userData);
2282           }
2283
2284           //// DEBUG
2285           //printf(" --- Offload API : Work Request Group member finished...\n");
2286           //printf("       wrGroupHandle = { numWRs = %d, fC = %d, state = %d }\n",
2287           //       wrGroup->numWRs, wrGroup->finishedCount, wrGroup->state
2288           //      );
2289           //printf("       wrQueuedHead = %p\n", wrQueuedHead);
2290
2291           // Check to see if this is the last work request in the group to complete
2292           if (wrGroup->state == WRGROUP_STATE_FULL && wrGroup->finishedCount >= wrGroup->numWRs) {
2293
2294             register void (*cbf)(void*) = ((wrGroup->callbackFunc != NULL) ? (wrGroup->callbackFunc) : (groupCallbackFunc));
2295
2296             // Check to see if there is a callback function
2297             if (cbf != NULL) {
2298
2299               // Call the callback function
2300               cbf(wrGroup->userData);
2301
2302               // Clean up the WRGroup structure
2303               wrGroup->state = WRGROUP_STATE_FREE;
2304
2305               // Add the WRGroup structure back into the free list
2306               if (__builtin_expect(wrGroupFreeTail == NULL, 0)) {
2307                 wrGroupFreeHead = wrGroupFreeTail = wrGroup;
2308               } else {
2309                 wrGroupFreeTail->next = wrGroup;
2310                 wrGroupFreeTail = wrGroup;
2311               }
2312
2313             } else {  // Otherwise, there is no callback function
2314
2315               // Mark the group as finished
2316               wrGroup->state = WRGROUP_STATE_FINISHED;
2317             }
2318
2319           }
2320
2321           // Clean up the work request structure and add it to the free list
2322           wrPtr->state = WORK_REQUEST_STATE_FREE;
2323           if (wrFreeTail == NULL) {
2324             wrFreeTail = wrFreeHead = wrPtr;
2325           } else {
2326             wrFreeTail->next = wrPtr;
2327             wrFreeTail = wrPtr;
2328           }
2329
2330         }
2331
2332         // Check to see if there is a pending work request in the wrQueued list
2333         if (wrQueuedHead != NULL) {
2334
2335           // NOTE : The common case should be that any work request can go to any SPE (optimize for this)
2336
2337           // Remove the first entry in the list that has affinity for this SPE
2338           WorkRequest* wrEntry = wrQueuedHead;
2339           WorkRequest* wrEntryPrev = NULL;
2340           register int affinity = wrEntry->speAffinityMask;
2341           register int affinityMask = 0x01 << i;
2342           while (__builtin_expect((affinity & affinityMask) == 0x00, 0) && __builtin_expect(wrEntry != NULL, 1)) {
2343             wrEntryPrev = wrEntry;
2344             wrEntry = wrEntry->next;
2345             affinity = wrEntry->speAffinityMask;
2346           }
2347
2348           // Check to see if a work request was found
2349           if (__builtin_expect(wrEntry != NULL, 1)) {
2350
2351             //// DEBUG
2352             //printf("       ISSUING IMMEDIATE...\n");
2353
2354             // STATS
2355             #if PPE_STATS != 0
2356               wrImmedIssueCount++;
2357             #endif
2358
2359             // Set the speIndex and entryIndex of the work request
2360             wrEntry->speIndex = i;
2361             wrEntry->entryIndex = j;
2362
2363
2364             // TRACE
2365             #if ENABLE_TRACE != 0
2366               if (wrEntry->traceFlag) {
2367
2368                 register int numReadOnly = wrEntry->readOnlyLen;
2369                 register int numReadWrite = wrEntry->readWriteLen;
2370                 register int numWriteOnly = wrEntry->writeOnlyLen;
2371                 register DMAListEntry* dmaList = wrEntry->dmaList;
2372                 register int jj;
2373
2374                 printf("OffloadAPI :: [TRACE] :: (OffloadAPIProgress) processingSPEIndex = %d, sentIndex = %d\n",
2375                        i, j
2376                       );
2377
2378                 printf("OffloadAPI :: [TRACE] :: (OffloadAPIProgress) dmaList:\n");
2379                 for (jj = 0; jj < numReadOnly; jj++) {
2380                   printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (RO)\n",
2381                          jj, dmaList[jj].ea, dmaList[jj].size
2382                         );
2383                 }
2384                 for (; jj < numReadOnly + numReadWrite; jj++) {
2385                   printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (RW)\n",
2386                          jj, dmaList[jj].ea, dmaList[jj].size
2387                         );
2388                 }
2389                 for (; jj < numReadOnly + numReadWrite + numWriteOnly; jj++) {
2390                   printf("OffloadAPI :: [TRACE] ::                          entry %d = { ea = 0x%08x, size = %u } (WO)\n",
2391                          jj, dmaList[jj].ea, dmaList[jj].size
2392                         );
2393                 }
2394               }
2395             #endif
2396
2397
2398             // Send the work request
2399             sendSPEMessage(speThreads[i], j, wrEntry, SPE_MESSAGE_COMMAND_NONE, wrEntry->dmaList);
2400             
2401             // Remove the work request from the queued list
2402             if (__builtin_expect(wrEntryPrev == NULL, 1)) { // Was the head of the queue
2403               wrQueuedHead = wrEntry->next;
2404               if (wrQueuedHead == NULL) wrQueuedTail = NULL;
2405             } else if (__builtin_expect(wrEntry->next == NULL, 0)) { // was the tail of the queue
2406               wrQueuedTail = wrEntryPrev;
2407               if (wrQueuedTail == NULL)
2408                 wrQueuedHead = NULL;
2409               else
2410                 wrQueuedTail->next = NULL;
2411             } else { // was in the middle of the queue
2412               wrEntryPrev->next = wrEntry->next;
2413             }
2414             wrEntry->next = NULL;
2415
2416           } else { // Otherwise, just clear the entry
2417
2418             //// DEBUG
2419             //printf("       NOT - ISSUING IMMEDIATE...\n");
2420
2421             // Now that the work request has been moved to either the wrFree list or marked as
2422             //   finished, set the state of the message queue entry to clear so it can accempt
2423             //   another work request
2424             msg->state = SPE_MESSAGE_STATE_CLEAR;
2425
2426             // Re-add the message queue entry to the msgQEntryFreeList
2427             #if USE_MESSAGE_QUEUE_FREE_LIST != 0
2428               //MSGQEntryList* msgQEntry = &(__msgQEntries[speMessageQueueIndex + (i * NUM_SPE_THREADS)]);
2429               //MSGQEntryList* msgQEntry = &(__msgQEntries[i + (j * NUM_SPE_THREADS)]);
2430               MSGQEntryList* msgQEntry = &(__msgQEntries[i + (j * numSPEThreads)]);
2431               if (__builtin_expect(msgQEntry->next != NULL, 0)) {
2432                 printf(" --- OffloadAPI :: ERROR :: msgQEntry->next != NULL !!!!!\n");
2433                 msgQEntry->next = NULL;
2434               }
2435               if (msgQEntryFreeTail != NULL) {
2436                 msgQEntryFreeTail->next = msgQEntry;
2437                 msgQEntryFreeTail = msgQEntry;
2438               } else {
2439                 msgQEntryFreeHead = msgQEntryFreeTail = msgQEntry;
2440               }
2441               msgQListLen++;
2442             #endif
2443
2444           }
2445
2446         } else {  // Otherwise, there is not a pending work request so just clear the message queue entry
2447
2448           // Now that the work request has been moved to either the wrFree list or marked as
2449           //   finished, set the state of the message queue entry to clear so it can accempt
2450           //   another work request
2451           msg->state = SPE_MESSAGE_STATE_CLEAR;
2452
2453           // Re-add the message queue entry to the msgQEntryFreeList
2454           #if USE_MESSAGE_QUEUE_FREE_LIST != 0
2455             //MSGQEntryList* msgQEntry = &(__msgQEntries[speMessageQueueIndex + (i * NUM_SPE_THREADS)]);
2456             //MSGQEntryList* msgQEntry = &(__msgQEntries[i + (j * NUM_SPE_THREADS)]);
2457             MSGQEntryList* msgQEntry = &(__msgQEntries[i + (j * numSPEThreads)]);
2458             if (__builtin_expect(msgQEntry->next != NULL, 0)) {
2459               printf(" --- OffloadAPI :: ERROR :: msgQEntry->next != NULL !!!!!\n");
2460               msgQEntry->next = NULL;
2461             }
2462             if (msgQEntryFreeTail != NULL) {
2463               msgQEntryFreeTail->next = msgQEntry;
2464               msgQEntryFreeTail = msgQEntry;
2465             } else {
2466               msgQEntryFreeHead = msgQEntryFreeTail = msgQEntry;
2467             }
2468             msgQListLen++;
2469           #endif
2470
2471         } // end if (wrQueuedHead != NULL)
2472
2473       } // end if (received notification from SPE)
2474
2475     } // end for (j < SPE_MESSAGE_QUEUE_LENGTH)
2476   } // end for (i < SPE_NUM_THREADS)
2477
2478   // STATS
2479   #if PPE_STATS != 0
2480     timeval progress1end;
2481     gettimeofday(&progress1end, NULL);
2482
2483     // Calculate the time taken
2484     double startTimeD = (double)progress1start.tv_sec + ((double)progress1start.tv_usec / 1000000.0);
2485     double endTimeD = (double)progress1end.tv_sec + ((double)progress1end.tv_usec / 1000000.0);
2486     double timeDiff = endTimeD - startTimeD;
2487     progress1time += timeDiff;
2488
2489     iterCount++;
2490     iterCountCounter++;
2491   #endif
2492 }
2493
2494 #endif
2495
2496 #endif
2497
2498
2499 int getWorkRequestID(WRHandle wrHandle) {
2500   int rtn = ((wrHandle == NULL) ? (-1) : (wrHandle->id));
2501   return rtn;
2502 }
2503
2504 // TRACE
2505 #if ENABLE_TRACE != 0
2506   void enableTrace() { traceFlag = -1; }
2507   void disableTrace() { traceFlag = 0; }
2508 #else
2509   void enableTrace() { }
2510   void disableTrace() { }
2511 #endif
2512
2513
2514 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
2515 // Private Function Bodies
2516
2517
2518 void handleStopAndNotify(SPEThread* speThread, int stopCode) {
2519
2520   // TODO : Handle them... for now, since there aren't any, warn that one occured
2521   fprintf(stderr, "OffloadAPI :: WARNING : Unhandled stop-and-notify (stopCode = %d)\n", stopCode);
2522 }
2523
2524
2525 void* pthread_func(void* arg) {
2526
2527   SPEThread* speThread = (SPEThread*)arg;
2528   int rtnCode;
2529
2530   // Load the SPE Program into the SPE Context
2531   rtnCode = spe_program_load(speThread->speContext, &spert_main);
2532   if (rtnCode != 0) {
2533     fprintf(stderr, "OffloadAPI :: ERROR : Unable to load program into SPE Context\n");
2534     exit(EXIT_FAILURE);
2535   }
2536
2537   // Start the SPE run loop
2538   speThread->speEntry = SPE_DEFAULT_ENTRY;
2539   do {
2540
2541     // Start/Continue execution of the SPE Context
2542     rtnCode = spe_context_run(speThread->speContext,
2543                               &(speThread->speEntry),
2544                               0,
2545                               speThread->speData,
2546                               NULL,
2547                               &(speThread->stopInfo)
2548                              );
2549
2550     // React to a stop-and-notify from the SPE Context (if this is one)
2551     if (rtnCode > 0) handleStopAndNotify(speThread, rtnCode);
2552
2553   } while (rtnCode > 0);
2554
2555   if (rtnCode < 0) {
2556     fprintf(stderr, "OffloadAPI :: ERROR : SPE Threaded exited with rtnCode = %d\n", rtnCode);
2557     exit(EXIT_FAILURE);
2558   }
2559
2560   // Destroy the SPE Context
2561   rtnCode = spe_context_destroy(speThread->speContext);
2562   if (rtnCode != 0) {
2563     fprintf(stderr, "OffloadAPI :: ERROR : Unable to destroy SPE Context\n");
2564     exit(EXIT_FAILURE);
2565   }
2566
2567   pthread_exit(NULL);
2568   return NULL;
2569 }
2570
2571
2572 // Returns NULL on error... otherwise, returns a pointer to SPEData structure that was passed to the
2573 //   created thread.
2574 SPEThread* createSPEThread(SPEData *speData) {
2575
2576   int speDataCreated = 0;
2577
2578   // Check to see if the speData is NULL... if so, create a default
2579   if (speData == NULL) {
2580
2581     // Create the SPEData structure
2582     speData = (SPEData*)malloc_aligned(sizeof(SPEData), 16);
2583     if (speData == NULL) {
2584       fprintf(stderr, "OffloadAPI :: ERROR : createSPEThread() : error code 1.0\n");
2585       return NULL;
2586     }
2587
2588     // Create the Message Queue
2589     speData->messageQueue = (PPU_POINTER_TYPE)malloc_aligned(SPE_MESSAGE_QUEUE_BYTE_COUNT, 16);
2590     if ((void*)(speData->messageQueue) == NULL) {
2591       fprintf(stderr, " --- Offload API :: ERROR : createSPEThread() : Unable to allocate memory for message queue.\n");
2592       free_aligned(speData);
2593       return NULL;
2594     }
2595     memset((void*)speData->messageQueue, 0x00, SPE_MESSAGE_QUEUE_BYTE_COUNT);
2596     speData->messageQueueLength = SPE_MESSAGE_QUEUE_LENGTH;
2597
2598     #if SPE_NOTIFY_VIA_MAILBOX == 0
2599       // Create the notify queue
2600       speData->notifyQueue = (PPU_POINTER_TYPE)malloc_aligned(SPE_NOTIFY_QUEUE_BYTE_COUNT, 16);
2601       if ((void*)(speData->notifyQueue) == NULL) {
2602         fprintf(stderr, " --- Offload API :: ERROR : createSPEThread() : Unable to allocate memory for notification queue.\n");
2603         free_aligned((void*)(speData->messageQueue));
2604         free_aligned(speData);
2605         return NULL;
2606       }
2607       memset((void*)speData->notifyQueue, 0x00, SPE_NOTIFY_QUEUE_BYTE_COUNT);
2608     #endif
2609
2610     // Give this SPE a unique number
2611     speData->vID = vIDCounter;
2612     vIDCounter++;
2613
2614     // Set flag indicating that this function malloc'ed the speData structure
2615     speDataCreated = 1;
2616   }
2617
2618
2619   /// SDK 2.0 ///
2620   //
2621   //// Create the thread
2622   //speid_t speID = spe_create_thread((spe_gid_t)NULL,       // spe_gid_t (actually a void*) - The SPE Group ID for the Thread
2623   //                                  (spe_program_handle_t*)(&spert_main),    // spe_program_handle_t* - Pointer to SPE's function that should be executed (name in embedded object file, not function name from code)
2624   //                                  speData,                 // void* - Argument List
2625   //                                  (void*)NULL,             // void* - Evironment List
2626   //                                  (unsigned long)-1,       // unsigned long - Affinity Mask
2627   //                                  (int)0                   // int - Flags
2628   //                               );
2629   //
2630   //// Verify that the thread was actually created
2631   //if (speID == NULL) {
2632   //
2633   //  fprintf(stderr, "OffloadAPI :: ERROR : createSPEThread() : error code 2.0 --- spe_create_thread() returned %d...\n", speID);
2634   //
2635   //  // Clean up the speData structure if this function created it
2636   //  if (speDataCreated != 0 && speData != NULL) {
2637   //    if ((void*)(speData->messageQueue) != NULL) free_aligned((void*)(speData->messageQueue));
2638   //    free_aligned(speData);
2639   //  }
2640   //
2641   //  // Return failure
2642   //  return NULL;
2643   //}
2644   //
2645   //// Wait for the SPE thread to check in (with pointer to its message queue in its local store)
2646   //while (spe_stat_out_mbox(speID) <= 0);
2647   //unsigned int speMessageQueuePtr = spe_read_out_mbox(speID);
2648   //if (speMessageQueuePtr == 0) {
2649   //  fprintf(stderr, "OffloadAPI :: ERROR : SPE checked in with NULL value for Message Queue\n");
2650   //  exit(EXIT_FAILURE);
2651   //}
2652   //
2653   //// Wait for the thread to report it's _end
2654   //#if SPE_REPORT_END != 0
2655   //  while (spe_stat_out_mbox(speID) <= 0);
2656   //  unsigned int endValue = spe_read_out_mbox(speID);
2657   //  printf("SPE reported _end = 0x%08x\n", endValue);
2658   //#endif
2659   //
2660   //#if DEBUG_DISPLAY >= 1
2661   //  printf("---> SPE Checked in with 0x%08X\n", speMessageQueuePtr);
2662   //#endif
2663   //
2664   //// Create the SPEThread structure to be returned
2665   //SPEThread *rtn = new SPEThread;
2666   //rtn->speData = speData;
2667   //rtn->speID = speID;
2668   //rtn->messageQueuePtr = speMessageQueuePtr;
2669   //rtn->msgIndex = 0;
2670   //rtn->counter = 0;
2671   //
2672   //return rtn;
2673   //
2674
2675
2676   /// SDK 2.1 ///
2677
2678   SPEThread* speThread = new SPEThread;
2679
2680   // Create the SPE Context
2681   speThread->speContext = spe_context_create(0, NULL);
2682   if (speThread->speContext == NULL) {
2683     fprintf(stderr, "OffloadAPI :: ERROR : Unable to create SPE Context\n");
2684     exit(EXIT_FAILURE);
2685   }
2686
2687   // Create the pthread for the SPE Thread
2688   int rtnCode = pthread_create(&(speThread->pThread), NULL, pthread_func, speThread);
2689   if (rtnCode != 0) {
2690     fprintf(stderr, "OffloadAPI :: ERROR : Unable to create pthread (rtnCode = %d)\n", rtnCode);
2691     exit(EXIT_FAILURE);
2692   }
2693
2694   // Wait for the SPE thread to check in (with pointer to its message queue in its local store)
2695   while (spe_out_mbox_status(speThread->speContext) == 0);
2696   spe_out_mbox_read(speThread->speContext, &(speThread->messageQueuePtr), 1);
2697   if (speThread->messageQueuePtr == NULL) {
2698     fprintf(stderr, "OffloadAPI :: Error : SPE checked in with NULL value for Message Queue\n");
2699     exit(EXIT_FAILURE);
2700   }
2701
2702   // Wait for the SPE thread to report it's _end value
2703   #if SPE_REPORT_END != 0
2704     unsigned int endValue;
2705     while (spe_out_mbox_status(speThread->speContext) == 0);
2706     spe_out_mbox_read(speThread->speContext, &endValue, 1);
2707     printf("SPE reported _end = 0x%08x\n", endValue);
2708   #endif
2709
2710   // Finish filling in the SPEThread structure
2711   // NOTE: The speEntry and stopInfo fields should not be written to.  The pthread will fillin and use
2712   //   these fields.  Writing to them here will create a race condition.  Badness!
2713   speThread->speData = speData;
2714   speThread->msgIndex = 0;
2715   speThread->counter = 0;
2716
2717   return speThread;
2718 }
2719
2720
2721 // Returns NULL on error... otherwise, returns a pointer to SPEData structure that was passed to the
2722 //   created thread.
2723 SPEThread** createSPEThreads(SPEThread **speThreads, int numThreads) {
2724
2725   int speDataCreated = 0;
2726
2727   // Verify the parameters
2728   if (speThreads == NULL || numThreads < 1)
2729     return NULL;
2730
2731   // Create the Message Queues
2732   for (int i = 0; i < numThreads; i++) {
2733
2734     // Create the SPEThread structure if it does not already exist
2735     if (speThreads[i] == NULL) speThreads[i] = new SPEThread;
2736     if (speThreads[i] == NULL) {
2737       printf("OffloadAPI :: ERROR :: Unable to allocate memory for SPEThread structure... Exiting.\n");
2738       exit(EXIT_FAILURE);
2739     }
2740
2741     // Create the speData structure
2742     speThreads[i]->speData = (SPEData*)malloc_aligned(SIZEOF_16(SPEData), 16);
2743     if (speThreads[i]->speData == NULL) {
2744       printf("OffloadAPI :: ERROR :: Unable to allocate memory for SPEData structure... Exiting.\n");
2745       exit(EXIT_FAILURE);
2746     }
2747
2748     // Create the message queue
2749     speThreads[i]->speData->messageQueue = (PPU_POINTER_TYPE)malloc_aligned(SPE_MESSAGE_QUEUE_BYTE_COUNT, 16);
2750     if ((void*)(speThreads[i]->speData->messageQueue) == NULL) {
2751       fprintf(stderr, " --- Offload API :: ERROR : createSPEThreads() : Unable to allocate memory for message queue.\n");
2752       exit(EXIT_FAILURE);
2753     }
2754     memset((void*)speThreads[i]->speData->messageQueue, 0x00, SPE_MESSAGE_QUEUE_BYTE_COUNT);
2755     speThreads[i]->speData->messageQueueLength = SPE_MESSAGE_QUEUE_LENGTH;
2756
2757     #if SPE_NOTIFY_VIA_MAILBOX == 0
2758       // Create the notify queue
2759       speThreads[i]->speData->notifyQueue = (PPU_POINTER_TYPE)malloc_aligned(SPE_NOTIFY_QUEUE_BYTE_COUNT, 16);
2760       if ((void*)(speThreads[i]->speData->notifyQueue) == NULL) {
2761         fprintf(stderr, " --- Offload API :: ERROR : createSPEThreads() : Unable to allocate memory for notification queue.\n");
2762         exit(EXIT_FAILURE);
2763       }
2764       memset((void*)speThreads[i]->speData->notifyQueue, 0x00, SPE_NOTIFY_QUEUE_BYTE_COUNT);
2765     #endif
2766
2767     // Give the SPE a unique id
2768     speThreads[i]->speData->vID = vIDCounter;
2769     vIDCounter++;
2770   }
2771
2772   /// SDK 2.0 ///
2773   //
2774   //// Create all the threads at once
2775   //for (int i = 0; i < numThreads; i++) {
2776   //
2777   //  speid_t speID = spe_create_thread((spe_gid_t)NULL,         // spe_gid_t (actually a void*) - The SPE Group ID for the Thread
2778   //                                    (spe_program_handle_t*)(&spert_main),    // spe_program_handle_t* - Pointer to SPE's function that should be executed (name in embedded object file, not function name from code)
2779   //                                    (void*)speThreads[i]->speData, // void* - Argument List
2780   //                                    (void*)NULL,             // void* - Evironment List
2781   //                                    (unsigned long)-1,       // unsigned long - Affinity Mask
2782   //                                    (int)0                   // int - Flags
2783   //                                   );
2784   //
2785   //  // Verify that the thread was actually created
2786   //  if (speID == NULL) {
2787   //    fprintf(stderr, "OffloadAPI :: ERROR : createSPEThreads() : error code 2.0 --- spe_create_thread() returned %d...\n", speID);
2788   //    exit(EXIT_FAILURE);
2789   //  }
2790   //
2791   //  // Store the speID
2792   //  speThreads[i]->speID = speID;
2793   //}
2794   //
2795   //// Wait for all the threads to check in (with pointer to its message queue in its local store)
2796   //for (int i = 0; i < numThreads; i++) {
2797   //
2798   //  while (spe_stat_out_mbox(speThreads[i]->speID) <= 0);
2799   //  unsigned int speMessageQueuePtr = spe_read_out_mbox(speThreads[i]->speID);
2800   //  if (speMessageQueuePtr == 0) {
2801   //   fprintf(stderr, "OffloadAPI :: ERROR : SPE checked in with NULL value for Message Queue\n");
2802   //   exit(EXIT_FAILURE);
2803   //  }
2804   //  speThreads[i]->messageQueuePtr = speMessageQueuePtr;
2805   //
2806   //  #if DEBUG_DISPLAY >= 1
2807   //    printf("---> SPE Checked in with 0x%08X\n", speMessageQueuePtr);
2808   //  #endif
2809   //
2810   //  // Wait for the thread to report it's _end
2811   //  #if SPE_REPORT_END != 0
2812   //    while (spe_stat_out_mbox(speThreads[i]->speID) <= 0);
2813   //    unsigned int endValue = spe_read_out_mbox(speThreads[i]->speID);
2814   //    printf("SPE_%d reported &(_end) = 0x%08x\n", i, endValue);
2815   //  #endif
2816   //}
2817   //
2818   //// Finish filling in the speThreads array
2819   //for (int i = 0; i < numThreads; i++) {
2820   //  speThreads[i]->msgIndex = 0;
2821   //  speThreads[i]->counter = 0;
2822   //}
2823   //
2824
2825
2826   /// SDK 2.1 ///
2827
2828   // For each of the SPE Threads...
2829   for (int i = 0; i < numThreads; i++) {
2830
2831     // Create the SPE Context
2832     speThreads[i]->speContext = spe_context_create(0, NULL);
2833     if (speThreads[i]->speContext == NULL) {
2834       printf("OffloadAPI :: ERROR : Unable to create SPE Context\n");
2835       exit(EXIT_FAILURE);
2836     }
2837
2838     // Create the pthread for the SPE Thread
2839     int rtnCode = pthread_create(&(speThreads[i]->pThread), NULL, pthread_func, speThreads[i]);
2840     if (rtnCode != 0) {
2841       fprintf(stderr, "OffloadAPI :: ERROR : Unable to create pthread (rtnCode = %d)\n", rtnCode);
2842       exit(EXIT_FAILURE);
2843     }
2844   }
2845
2846   // For each of the SPE Threads...
2847   // NOTE : Done as a separate loop since SPEs need startup time (i.e. don't create and wait for the first
2848   //   SPE thread before creating the second... create all then wait all... by the time the last is created
2849   //   hopefully the first has checked in).
2850   for (int i = 0; i < numThreads; i++) {
2851
2852     // Wait for the SPE Thread to check in
2853     while (spe_out_mbox_status(speThreads[i]->speContext) == 0);
2854     spe_out_mbox_read(speThreads[i]->speContext, &(speThreads[i]->messageQueuePtr), 1);
2855     if (speThreads[i]->messageQueuePtr == NULL) {
2856       fprintf(stderr, "OffloadAPI :: Error : SPE checked in with NULL value for Message Queue\n");
2857       exit(EXIT_FAILURE);
2858     }
2859
2860     // Wait for the SPE thread to report it's _end value
2861     #if SPE_REPORT_END != 0
2862       unsigned int endValue;
2863       while (spe_out_mbox_status(speThreads[i]->speContext) == 0);
2864       spe_out_mbox_read(speThreads[i]->speContext, &endValue, 1);
2865       printf("SPE reported _end = 0x%08x\n", endValue);
2866     #endif
2867
2868     // Finish filling in the SPEThread structure
2869     // NOTE: The speEntry and stopInfo fields should not be written to.  The pthread will fillin and use
2870     //   these fields.  Writing to them here will create a race condition.  Badness!
2871     speThreads[i]->msgIndex = 0;
2872     speThreads[i]->counter = 0;
2873   }
2874
2875
2876   return speThreads;
2877 }
2878
2879
2880 int sendSPEMessage(SPEThread *speThread, WorkRequest *wrPtr, int command) {
2881
2882   // TODO : Re-write these checks now that command no longer use the message queue
2883
2884   // Verify the parameters
2885   //if (speThread == NULL) return -1;
2886
2887   //if (wrPtr != NULL && wrPtr->funcIndex < 0 && command == SPE_MESSAGE_COMMAND_NONE)
2888   //  return -1;
2889
2890   // Force the command value to a valid command
2891   //if (command < SPE_MESSAGE_COMMAND_MIN || command > SPE_MESSAGE_COMMAND_MAX)
2892   //  command = SPE_MESSAGE_COMMAND_NONE;
2893
2894   /*
2895   if (wrPtr != NULL) {
2896
2897     // Check to see if there is no work request and no command (if so, return without doing anything)
2898     if (wrPtr->funcIndex < 0 &&
2899         wrPtr->readWritePtr == NULL && wrPtr->readOnlyPtr == NULL && wrPtr->writeOnlyPtr == NULL &&
2900         command == SPE_MESSAGE_COMMAND_NONE
2901        )
2902       return -1;
2903
2904     // Make sure the readWriteLen is non-negative
2905     if (wrPtr->readWriteLen < 0) {
2906       #if DEBUG_DISPLAY >= 1
2907         fprintf(stderr, "OffloadAPI :: WARNING :: sendSPEMessage() received readWriteLen < 0... forcing to 0...\n");
2908       #endif
2909       wrPtr->readWriteLen = 0;
2910     }
2911
2912     // Make sure the readOnlyLen is non-negative
2913     if (wrPtr->readOnlyLen < 0) {
2914       #if DEBUG_DISPLAY >= 1
2915         fprintf(stderr, "OffloadAPI :: WARNING :: sendSPEMessage() received readOnlyLen < 0... forcing to 0...\n");
2916       #endif
2917       wrPtr->readOnlyLen = 0;
2918     }
2919
2920     // Make sure the writeOnlyLen is non-negative
2921     if (wrPtr->writeOnlyLen < 0) {
2922       #if DEBUG_DISPLAY >= 1
2923         fprintf(stderr, "OffloadAPI :: WARNING :: sendSPEMessage() received writeOnlyLen < 0... forcing to 0...\n");
2924       #endif
2925       wrPtr->writeOnlyLen = 0;
2926     }
2927
2928     if ((wrPtr->flags & WORK_REQUEST_FLAGS_LIST) == 0x00) {  // standard send
2929       // Force the xxxPtr and xxxLen pairs so they match (i.e. - 'xxxPtr != NULL && xxxLen <= 0' is not good)
2930       if (wrPtr->readWritePtr == NULL) wrPtr->readWriteLen = 0;
2931       if (wrPtr->readWriteLen <= 0) { wrPtr->readWritePtr = NULL; wrPtr->readWriteLen = 0; }
2932       if (wrPtr->readOnlyPtr == NULL) wrPtr->readOnlyLen = 0;
2933       if (wrPtr->readOnlyLen <= 0) { wrPtr->readOnlyPtr = NULL; wrPtr->readOnlyLen = 0; }
2934       if (wrPtr->writeOnlyPtr == NULL) wrPtr->writeOnlyLen = 0;
2935       if (wrPtr->writeOnlyLen <= 0) { wrPtr->writeOnlyPtr = NULL; wrPtr->writeOnlyLen = 0; }
2936     } else {  // dma list send
2937       if (wrPtr->readWritePtr == NULL) return -1;  // DMA list send with no list
2938       if (wrPtr->readWriteLen <= 0 && wrPtr->readOnlyLen <= 0 && wrPtr->writeOnlyLen <= 0) return -1; // no list item info
2939     }
2940   }
2941   */
2942
2943   // Find the next available index
2944   for (int i = 0; i < SPE_MESSAGE_QUEUE_LENGTH; i++) {
2945
2946     int index = (speThread->msgIndex + i) % SPE_MESSAGE_QUEUE_LENGTH;
2947     volatile SPEMessage* msg = (SPEMessage*)(((char*)speThread->speData->messageQueue) + (index * SIZEOF_16(SPEMessage)));
2948     
2949     if (msg->state == SPE_MESSAGE_STATE_CLEAR) {
2950
2951       // TRACE
2952       #if ENABLE_TRACE != 0
2953         if (wrPtr->traceFlag) {
2954           printf("PPE :: [TRACE] :: sendSPEMessage() :: message queue before new entry...\n");
2955           displayMessageQueue(speThread);
2956         }
2957       #endif
2958
2959       if (__builtin_expect(wrPtr == NULL, 0)) {  // NOTE: Common case should be user's code making work
2960         msg->funcIndex = -1;                     //   requests (not commands going to the SPE Runtime)
2961         msg->readWritePtr = (PPU_POINTER_TYPE)NULL;
2962         msg->readWriteLen = 0;
2963         msg->readOnlyPtr = (PPU_POINTER_TYPE)NULL;
2964         msg->readOnlyLen = 0;
2965         msg->writeOnlyPtr = (PPU_POINTER_TYPE)NULL;
2966         msg->writeOnlyLen = 0;
2967         msg->flags = WORK_REQUEST_FLAGS_NONE;
2968         msg->totalMem = 0;
2969
2970         // TRACE
2971         #if ENABLE_TRACE != 0
2972           msg->traceFlag = 0;
2973         #endif
2974
2975       } else {
2976         msg->funcIndex = wrPtr->funcIndex;
2977         msg->readWritePtr = (PPU_POINTER_TYPE)(wrPtr->readWritePtr);
2978         msg->readWriteLen = wrPtr->readWriteLen;
2979         msg->readOnlyPtr = (PPU_POINTER_TYPE)(wrPtr->readOnlyPtr);
2980         msg->readOnlyLen = wrPtr->readOnlyLen;
2981         msg->writeOnlyPtr = (PPU_POINTER_TYPE)(wrPtr->writeOnlyPtr);
2982         msg->writeOnlyLen = wrPtr->writeOnlyLen;
2983         msg->flags = wrPtr->flags;
2984
2985         // Copy the DMA list (if is list WR and the dma list is small enought... otherwise, don't bother)
2986         if ((wrPtr->flags & WORK_REQUEST_FLAGS_LIST) == WORK_REQUEST_FLAGS_LIST) {
2987           register int dmaListSize = wrPtr->readWriteLen + wrPtr->readOnlyLen + wrPtr->writeOnlyLen;
2988           if (dmaListSize <= SPE_DMA_LIST_LENGTH) {
2989             register volatile DMAListEntry* msgDMAList = msg->dmaList;
2990             register DMAListEntry* wrDMAList = (DMAListEntry*)(wrPtr->readWritePtr);
2991             for (int i = 0; i < dmaListSize; i++) {
2992               msgDMAList[i].ea = wrDMAList[i].ea;
2993               msgDMAList[i].size = wrDMAList[i].size;
2994             }
2995           }
2996         }
2997
2998         // Calculate the total amount of memory that will be needed on the SPE for this message/work-request
2999         if ((msg->flags & WORK_REQUEST_FLAGS_LIST) == WORK_REQUEST_FLAGS_LIST) {
3000           // The memory needed is the size of the DMA list rounded up times 2 (two lists) and the size of each
3001           //   of the individual entries in that list all rounded up
3002           register int numEntries = wrPtr->readWriteLen + wrPtr->readOnlyLen + wrPtr->writeOnlyLen;
3003           msg->totalMem = ROUNDUP_16(sizeof(DMAListEntry) * numEntries);
3004           msg->totalMem *= 2;  // Second DMA List within SPE's local store (with LS pointers)
3005           for (int entryIndex = 0; entryIndex < numEntries; entryIndex++)
3006             msg->totalMem += ROUNDUP_16(((DMAListEntry*)(wrPtr->readWritePtr))[entryIndex].size);
3007         } else {
3008           // The memory needed is the size of the sum of the three buffers each rounded up
3009           msg->totalMem = ROUNDUP_16(wrPtr->readWriteLen) + ROUNDUP_16(wrPtr->readOnlyLen) + ROUNDUP_16(wrPtr->writeOnlyLen);
3010         }
3011
3012         // TRACE
3013         #if ENABLE_TRACE != 0
3014           msg->traceFlag = ((wrPtr->traceFlag) ? (-1) : (0));  // force -1 or 0
3015         #endif
3016       }
3017       msg->state = SPE_MESSAGE_STATE_SENT;
3018       msg->command = command;
3019       msg->wrPtr = (PPU_POINTER_TYPE)wrPtr;
3020
3021       // NOTE: Important that the counter be the last then set (the change in this value is what prompts the
3022       //   SPE to consider the entry a new entry... even if the state has been set to SENT).
3023       // NOTE: Only change the value of msg->counter once so the SPE is not confused (i.e. - don't increment
3024       //   and then check msg->counter direclty).
3025       int tmp0 = msg->counter0;
3026       int tmp1 = msg->counter1;
3027       tmp0++; if (tmp0 > 255) tmp0 = 0;
3028       tmp1++; if (tmp1 > 255) tmp1 = 0;
3029       __asm__ ("sync");
3030       msg->counter0 = tmp0;
3031       msg->counter1 = tmp1;
3032
3033       // TRACE
3034       #if ENABLE_TRACE != 0
3035         if (wrPtr->traceFlag) {
3036           printf("  sendSPEMessage() : sending message to queue @ %p, slot %d (@ %p; SIZEOF_16(SPEMessage) = %d)...\n",
3037                  speThread->speData->messageQueue,
3038                  index,
3039                  msg,
3040                  SIZEOF_16(SPEMessage)
3041                 );
3042           printf("  sendSPEMessage() : message[%d] = {", index);
3043           printf(" funcIndex = %d", msg->funcIndex);
3044           printf(", readWritePtr = %u", msg->readWritePtr);
3045           //printf(", readWriteLen = %d", msg->readWriteLen);
3046           printf(", readOnlyPtr = %u", msg->readOnlyPtr);
3047           //printf(", readOnlyLen = %d", msg->readOnlyLen);
3048           printf(", writeOnlyPtr = %u", msg->writeOnlyPtr);
3049           //printf(", writeOnlyLen = %d", msg->writeOnlyLen);
3050           //printf(", flags = 0x08x", msg->flags);
3051           printf(", wrPtr = %u", msg->wrPtr);
3052           printf(", state = %d", msg->state);
3053           //printf(", command = %d", msg->command);
3054           //printf(", counter = %d", msg->counter);
3055           printf(" }\n");
3056         }
3057       #endif
3058
3059       speThread->msgIndex = (index + 1) % SPE_MESSAGE_QUEUE_LENGTH;
3060
3061       return index;
3062     } // end if (msg->state == SPE_MESSAGE_STATE_CLEAR)
3063   } // end for (loop through message queue entries)
3064
3065   // If execution reaches here, an available slot in the message queue was not found
3066   return -1;
3067 }
3068
3069
3070 WorkRequest* createWRHandles(int numHandles) {
3071
3072
3073   // DEBUG
3074   //printf("OffloadAPI :: [DEBUG] :: createWRHandles(%d) - Called...\n", numHandles);
3075
3076
3077   // Verify the parameter
3078   if (numHandles <= 0) return NULL;
3079
3080   #if DEBUG_DISPLAY >= 1
3081     printf(" --- Offload API ::: Creating %d more WRHandles ---\n", numHandles);
3082   #endif
3083
3084   // Allocate the memory for all the WRHandles at once
3085   WorkRequest *workReqs = new WorkRequest[numHandles];
3086
3087   // Initialize the entries
3088   memset(workReqs, 0, numHandles * sizeof(WorkRequest));
3089   for (int i = 0; i < numHandles; i++) {
3090     workReqs[i].isFirstInSet = ((i == 0) ? (TRUE) : (FALSE));
3091     workReqs[i].state = WORK_REQUEST_STATE_FREE;
3092     workReqs[i].next = ((i < (numHandles - 1)) ? (&(workReqs[i+1])) : (NULL));
3093   }
3094
3095   // Add this allocated WRHandle array to the allocatedWRHandlesList
3096   if (allocatedWRHandlesList == NULL) {
3097     allocatedWRHandlesList = new PtrList;
3098     allocatedWRHandlesList->ptr = (void*)workReqs;
3099     allocatedWRHandlesList->next = NULL;
3100   } else {
3101     PtrList *entry = allocatedWRHandlesList;
3102     while (entry->next != NULL) entry = entry->next;
3103     entry->next = new PtrList;
3104     entry = entry->next;
3105     entry->ptr = (void*)workReqs;
3106     entry->next = NULL;
3107   }
3108
3109   return workReqs;
3110 }
3111
3112
3113 WRGroup* createWRGroupHandles(int numHandles) {
3114
3115
3116   // DEBUG
3117   //printf("OffloadAPI :: [DEBUG] :: createWRGroupHandles(%d) - Called...\n", numHandles);
3118
3119
3120   // Verify the parameter
3121   if (numHandles <= 0) return NULL;
3122
3123   #if DEBUG_DISPLAY >= 1
3124     printf(" --- Offload API ::: Creating %d more WRGroupHandles ---\n", numHandles);
3125   #endif
3126
3127   // Allocate the memory for all the WRGroupHandles at once
3128   WRGroup* groups = new WRGroup[numHandles];
3129
3130   // Initialize the entries
3131   memset(groups, 0, numHandles * sizeof(WRGroup));
3132   for (int i = 0; i < numHandles; i++) {
3133     groups[i].state = WRGROUP_STATE_FREE;
3134     groups[i].next = ((i < (numHandles - 1)) ? (&(groups[i+1])) : (NULL));
3135   }
3136
3137   // Add this allocated WRHandle array to the allocatedWRHandlesList
3138   if (allocatedWRGroupHandlesList == NULL) {
3139     allocatedWRGroupHandlesList = new PtrList;
3140     allocatedWRGroupHandlesList->ptr = (void*)groups;
3141     allocatedWRGroupHandlesList->next = NULL;
3142   } else {
3143     PtrList *entry = allocatedWRGroupHandlesList;
3144     while (entry->next != NULL) entry = entry->next;
3145     entry->next = new PtrList;
3146     entry = entry->next;
3147     entry->ptr = (void*)groups;
3148     entry->next = NULL;
3149   }
3150
3151   return groups;
3152 }
3153
3154
3155 void OffloadAPIDisplayConfig(FILE* fout) {
3156
3157   // Make sure fout points somewhere
3158   if (fout == NULL) fout = stdout;
3159
3160   // Dump the Offload API's configuration parameters to the file specified
3161   fprintf(fout, "OffloadAPI :: [CONFIG] :: PPE:\n");
3162   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Threads:\n");
3163   fprintf(fout, "OffloadAPI :: [CONFIG] ::     NUM_SPE_THREADS: %d\n", NUM_SPE_THREADS);
3164   #if NUM_SPE_THREADS <= 0
3165     fprintf(fout, "OffloadAPI :: [CONFIG] ::     numSPEThreads: %d\n", numSPEThreads);
3166   #endif
3167   fprintf(fout, "OffloadAPI :: [CONFIG] ::     CREATE_EACH_THREAD_ONE_BY_ONE: %d\n", CREATE_EACH_THREAD_ONE_BY_ONE);
3168   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Debugging:\n");
3169   fprintf(fout, "OffloadAPI :: [CONFIG] ::     DEBUG_DISPLAY: %d\n", DEBUG_DISPLAY);
3170   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Stats:\n");
3171   fprintf(fout, "OffloadAPI :: [CONFIG] ::     PPE_STATS: %d\n", PPE_STATS);
3172   fprintf(fout, "OffloadAPI :: [CONFIG] :: SPE:\n");
3173   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Queues:\n");
3174   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_MESSAGE_QUEUE_LENGTH: %d\n", SPE_MESSAGE_QUEUE_LENGTH);
3175   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_MESSAGE_QUEUE_BYTE_COUNT: %d\n", SPE_MESSAGE_QUEUE_BYTE_COUNT);
3176   fprintf(fout, "OffloadAPI :: [CONFIG] ::     DOUBLE_BUFFER_MESSAGE_QUEUE: %d\n", DOUBLE_BUFFER_MESSAGE_QUEUE);
3177   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_NOTIFY_VIA_MAILBOX: %d\n", SPE_NOTIFY_VIA_MAILBOX);
3178   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_NOTIFY_QUEUE_BYTE_COUNT: %d\n", SPE_NOTIFY_QUEUE_BYTE_COUNT);
3179   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Static DMA Lists:\n");
3180   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_DMA_LIST_LENGTH: %d\n", SPE_DMA_LIST_LENGTH);
3181   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_DMA_LIST_ENTRY_MAX_LENGTH: %d\n", SPE_DMA_LIST_ENTRY_MAX_LENGTH);
3182   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Memory:\n");
3183   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_TOTAL_MEMORY_SIZE: %d\n", SPE_TOTAL_MEMORY_SIZE);
3184   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_USE_OWN_MEMSET: %d\n", SPE_USE_OWN_MEMSET);
3185   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_USE_OWN_MALLOC: %d\n", SPE_USE_OWN_MALLOC);
3186   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_MEMORY_BLOCK_SIZE: %d\n", SPE_MEMORY_BLOCK_SIZE);
3187   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_RESERVED_STACK_SIZE: %d\n", SPE_RESERVED_STACK_SIZE);
3188   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_MINIMUM_HEAP_SIZE: %d\n", SPE_MINIMUM_HEAP_SIZE);
3189   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_ZERO_WRITE_ONLY_MEMORY: %d\n", SPE_ZERO_WRITE_ONLY_MEMORY);
3190   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Scheduler Controls:\n");
3191   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_USE_STATE_LOOKUP_TABLE: %d\n", SPE_USE_STATE_LOOKUP_TABLE);
3192   fprintf(fout, "OffloadAPI :: [CONFIG] ::     LIMIT_READY: %d\n", LIMIT_READY);
3193   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Tracing:\n");
3194   fprintf(fout, "OffloadAPI :: [CONFIG] ::     ENABLE_TRACE: %d\n", ENABLE_TRACE);
3195   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Debugging:\n");
3196   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_DEBUG_DISPLAY: %d\n", SPE_DEBUG_DISPLAY);
3197   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_DEBUG_DISPLAY_STILL_ALIVE: %d\n", SPE_DEBUG_DISPLAY_STILL_ALIVE);
3198   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_DEBUG_DISPLAY_NO_PROGRESS: %d\n", SPE_DEBUG_DISPLAY_NO_PROGRESS);
3199   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_REPORT_END: %d\n", SPE_REPORT_END);
3200   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_NOTIFY_ON_MALLOC_FAILURE: %d\n", SPE_NOTIFY_ON_MALLOC_FAILURE);
3201   fprintf(fout, "OffloadAPI :: [CONFIG] ::   Stats:\n");
3202   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_TIMING: %d\n", SPE_TIMING);
3203   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_STATS: %d\n", SPE_STATS);
3204   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_STATS1: %d\n", SPE_STATS1);
3205   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_STATS2: %d\n", SPE_STATS2);
3206   fprintf(fout, "OffloadAPI :: [CONFIG] ::   User Tags:\n");
3207   fprintf(fout, "OffloadAPI :: [CONFIG] ::     SPE_NUM_USER_TAGS: %d\n", SPE_NUM_USER_TAGS);
3208 }
3209
3210
3211 #if SPE_TIMING != 0
3212
3213 void openProjFile(char* name) {
3214
3215   char buf[256];
3216   buf[0] = '\0';
3217
3218   // Verify the parameter
3219   if (name == NULL || strlen(name) <= 0)
3220     name = "default";
3221
3222   // Create the file name
3223   sprintf(buf, "%s.cellRaw", name);
3224
3225   // Open the file for writing
3226   projFile = fopen(buf, "w+");
3227   if (__builtin_expect(projFile == NULL, 0)) {
3228     fprintf(stderr, " --- Offload API :: [WARNING] :: Unable to open timing file (\"%s\")...\n", buf);
3229     return;
3230   }
3231 }
3232
3233 void closeProjFile() {
3234
3235   if (projFile != NULL) {
3236
3237     // Flush the remaining buffer entries to the file
3238     flushProjBuf();
3239
3240     // Output the number of total entries
3241     register size_t bytesWritten = fwrite((void*)(&totalProjSampleCount), 1, sizeof(int), projFile);
3242     if (sizeof(int) != bytesWritten) {
3243       fprintf(stderr,
3244               " --- Offload API :: [WARNING] :: Incorrect number of bytes written when writing entry count (%d of %d bytes)...\n",
3245               bytesWritten, sizeof(int)
3246              );
3247     }
3248
3249     // Close the file
3250     fclose(projFile);
3251   }
3252 }
3253
3254 void addProjEntry(SPENotify* notifyEntry, int speIndex, int funcIndex) {
3255
3256   // Make sure the file is open
3257   if (projFile == NULL) return;
3258
3259   // Make sure there is room in the buffer for this entry (if not, flush)
3260   if (projBufCount >= PROJ_BUF_SIZE)
3261     flushProjBuf();
3262
3263   // Add the entry to the buffer
3264   //projBuf[projBufCount].startTime = notifyEntry->startTime;
3265   //projBuf[projBufCount].runTime = notifyEntry->runTime;
3266   projBuf[projBufCount].speIndex = speIndex;
3267   projBuf[projBufCount].funcIndex = funcIndex;
3268
3269   projBuf[projBufCount].recvTimeStart = notifyEntry->recvTimeStart;
3270   projBuf[projBufCount].recvTimeEnd = notifyEntry->recvTimeEnd;
3271   projBuf[projBufCount].preFetchingTimeStart = notifyEntry->preFetchingTimeStart;
3272   projBuf[projBufCount].preFetchingTimeEnd = notifyEntry->preFetchingTimeEnd;
3273   projBuf[projBufCount].fetchingTimeStart = notifyEntry->fetchingTimeStart;
3274   projBuf[projBufCount].fetchingTimeEnd = notifyEntry->fetchingTimeEnd;
3275   projBuf[projBufCount].readyTimeStart = notifyEntry->readyTimeStart;
3276   projBuf[projBufCount].readyTimeEnd = notifyEntry->readyTimeEnd;
3277   projBuf[projBufCount].userTimeStart = notifyEntry->userTimeStart;
3278   projBuf[projBufCount].userTimeEnd = notifyEntry->userTimeEnd;
3279   projBuf[projBufCount].executedTimeStart = notifyEntry->executedTimeStart;
3280   projBuf[projBufCount].executedTimeEnd = notifyEntry->executedTimeEnd;
3281   projBuf[projBufCount].commitTimeStart = notifyEntry->commitTimeStart;
3282   projBuf[projBufCount].commitTimeEnd = notifyEntry->commitTimeEnd;
3283
3284   projBuf[projBufCount].userTime0Start = notifyEntry->userTime0Start;
3285   projBuf[projBufCount].userTime0End = notifyEntry->userTime0End;
3286   projBuf[projBufCount].userTime1Start = notifyEntry->userTime1Start;
3287   projBuf[projBufCount].userTime1End = notifyEntry->userTime1End;
3288   projBuf[projBufCount].userTime2Start = notifyEntry->userTime2Start;
3289   projBuf[projBufCount].userTime2End = notifyEntry->userTime2End;
3290
3291   projBuf[projBufCount].userAccumTime0 = notifyEntry->userAccumTime0;
3292   projBuf[projBufCount].userAccumTime1 = notifyEntry->userAccumTime1;
3293   projBuf[projBufCount].userAccumTime2 = notifyEntry->userAccumTime2;
3294   projBuf[projBufCount].userAccumTime3 = notifyEntry->userAccumTime3;
3295
3296   // Increment the projBufCount so it points to the next projBuf entry
3297   projBufCount++;
3298
3299   // Increment the counter for the number of total samples
3300   totalProjSampleCount++;
3301 }
3302
3303