Merged eb/gcell-wip2 rev 10130:10152 into trunk.
[debian/gnuradio] / gcell / lib / runtime / spu / gc_main.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 // #define ENABLE_GC_LOGGING    // define to enable logging
23
24 #include <spu_intrinsics.h>
25 #include <spu_mfcio.h>
26 #include <sync_utils.h>
27 #include "gc_spu_config.h"
28 #include "spu_buffers.h"
29 #include <gcell/gc_spu_args.h>
30 #include <gcell/gc_job_desc.h>
31 #include <gcell/gc_mbox.h>
32 #include <gcell/gc_declare_proc.h>
33 #include <gcell/spu/gc_jd_queue.h>
34 #include <gcell/spu/gc_random.h>
35 #include <gcell/spu/gc_delay.h>
36
37 #include <string.h>
38 #include <assert.h>
39 #include <stdio.h>
40
41
42 #define MIN(a,b) ((a) < (b) ? (a) : (b))
43 #define MAX(a,b) ((a) > (b) ? (a) : (b))
44
45 //! round x down to p2 boundary (p2 must be a power-of-2)
46 #define ROUND_DN(x, p2) ((x) & ~((p2)-1))
47
48 //! round x up to p2 boundary (p2 must be a power-of-2)
49 #define ROUND_UP(x, p2) (((x)+((p2)-1)) & ~((p2)-1))
50
51
52 #define USE_LLR_LOST_EVENT      0       // define to 0 or 1
53
54 int                     gc_sys_tag;     // tag for misc DMA operations
55 static gc_spu_args_t    spu_args;
56
57 static struct gc_proc_def *gc_proc_def; // procedure entry points
58
59 // ------------------------------------------------------------------------
60
61 // state for DMA'ing arguments in and out
62
63 static int get_tag;             // 1 tag for job arg gets
64 static int put_tags;            // 2 tags for job arg puts
65
66 static int pb_idx = 0;          // current put buffer index (0 or 1)
67
68 // bitmask (bit per put buffer): bit is set if DMA is started but not complete
69 static int put_in_progress = 0;
70 #define PBI_MASK(_pbi_) (1 << (_pbi_))
71
72 // ------------------------------------------------------------------------
73
74 // our working copy of the completion info
75 static gc_comp_info_t   comp_info = {  
76   .in_use = 1,
77   .ncomplete = 0
78 };
79
80 static int ci_idx = 0;          // index of current comp_info
81 static int ci_tags;             // two consecutive dma tags
82
83 // ------------------------------------------------------------------------
84
85 /*
86  * Wait until EA copy of comp_info[idx].in_use is 0
87  */
88 static void
89 wait_for_ppe_to_be_done_with_comp_info(int idx)
90 {
91   char _tmp[256];
92   char *buf = (char *) ALIGN(_tmp, 128);        // get cache-aligned buffer
93   gc_comp_info_t *p = (gc_comp_info_t *) buf;
94
95   assert(sizeof(gc_comp_info_t) == 128);
96
97   do {
98     mfc_get(buf, spu_args.comp_info[idx], 128, gc_sys_tag, 0, 0);
99     mfc_write_tag_mask(1 << gc_sys_tag);
100     mfc_read_tag_status_all();
101     if (p->in_use == 0)
102       return;
103
104     gc_udelay(5);
105
106   } while (1);
107 }
108
109 static void
110 flush_completion_info(void)
111 {
112   // events: 0x3X
113
114   static int total_complete = 0;
115
116   if (comp_info.ncomplete == 0)
117     return;
118   
119   // ensure that PPE is done with the buffer we're about to overwrite
120   wait_for_ppe_to_be_done_with_comp_info(ci_idx);
121
122   // dma the comp_info out to PPE
123   int tag = ci_tags + ci_idx;
124   mfc_put(&comp_info, spu_args.comp_info[ci_idx], sizeof(gc_comp_info_t), tag, 0, 0);
125
126   // we need to wait for the completion info to finish, as well as
127   // any EA argument puts.
128
129   int tag_mask = 1 << tag;              // the comp_info tag
130   if (put_in_progress & PBI_MASK(0))
131     tag_mask |= (1 << (put_tags + 0));
132   if (put_in_progress & PBI_MASK(1))
133     tag_mask |= (1 << (put_tags + 1));
134
135   gc_log_write2(GCL_SS_SYS, 0x30, put_in_progress, tag_mask);
136
137   mfc_write_tag_mask(tag_mask);         // the tags we're interested in
138   mfc_read_tag_status_all();            // wait for DMA to complete
139   put_in_progress = 0;                  // mark them all complete
140
141   total_complete += comp_info.ncomplete;
142   gc_log_write4(GCL_SS_SYS, 0x31,
143                 put_in_progress, ci_idx, comp_info.ncomplete, total_complete);
144
145   // send PPE a message
146   spu_writech(SPU_WrOutIntrMbox, MK_MBOX_MSG(OP_JOBS_DONE, ci_idx));
147
148   ci_idx ^= 0x1;        // switch buffers
149   comp_info.in_use = 1;
150   comp_info.ncomplete = 0;
151 }
152
153 // ------------------------------------------------------------------------
154
155 static unsigned int backoff;            // current backoff value in clock cycles
156 static unsigned int _backoff_start;
157 static unsigned int _backoff_cap;
158
159 /*
160  * For 3.2 GHz SPE
161  *
162  * 12    4095 cycles    1.3 us
163  * 13    8191 cycles    2.6 us
164  * 14   16383 cycles    5.1 us
165  * 15   32767 cycles   10.2 us
166  * 16                  20.4 us
167  * 17                  40.8 us
168  * 18                  81.9 us
169  * 19                 163.8 us
170  * 20                 327.7 us
171  * 21                 655.4 us
172  */
173 static unsigned char log2_backoff_start[16] = {
174 // 1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16
175 // -------------------------------------------------------------
176   12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 16, 16
177 };
178   
179 static unsigned char log2_backoff_cap[16] = {
180 // 1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16
181 // -------------------------------------------------------------
182   17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 21, 21
183 };
184   
185 static void
186 backoff_init(void)
187 {
188   _backoff_cap   = (1 << (log2_backoff_cap[(spu_args.nspus - 1) & 0xf])) - 1;
189   _backoff_start = (1 << (log2_backoff_start[(spu_args.nspus - 1) & 0xf])) - 1;
190   
191   backoff = _backoff_start;
192 }
193
194 static void 
195 backoff_reset(void)
196 {
197   backoff = _backoff_start;
198 }
199
200 #if 0
201
202 static void
203 backoff_delay(void)
204 {
205   gc_cdelay(backoff);
206
207   // capped exponential backoff
208   backoff = ((backoff << 1) + 1) & _backoff_cap;
209 }
210
211 #else
212
213 #define RANDOM_WEIGHT   0.2
214
215 static void
216 backoff_delay(void)
217 {
218   gc_cdelay(backoff);
219
220   backoff = ((backoff << 1) + 1);
221   if (backoff > _backoff_cap)
222     backoff = _backoff_cap;
223
224   float r = (RANDOM_WEIGHT * (2.0 * (gc_uniform_deviate() - 0.5)));
225   backoff = backoff * (1.0 + r);
226 }
227
228 #endif
229
230 // ------------------------------------------------------------------------
231
232 static inline unsigned int
233 make_mask(int nbits)
234 {
235   return ~(~0 << nbits);
236 }
237
238 static unsigned int   dc_work;
239 static int            dc_put_tag;
240 static unsigned char *dc_ls_base;
241 static gc_eaddr_t     dc_ea_base;
242
243 // divide and conquer
244 static void
245 d_and_c(unsigned int offset, unsigned int len)
246 {
247   unsigned int mask = make_mask(len) << offset;
248   unsigned int t = mask & dc_work;
249   if (t == 0)           // nothing to do
250     return;
251   if (t == mask){       // got a match, generate dma
252     mfc_put(dc_ls_base + offset, dc_ea_base + offset, len, dc_put_tag, 0, 0);
253   }
254   else {                // bisect
255     len >>= 1;
256     d_and_c(offset, len);
257     d_and_c(offset + len, len);
258   }
259 }
260
261 // Handle the nasty case of a dma xfer that's less than 16 bytes long.
262 // len is guaranteed to be in [1, 15]
263
264 static void
265 handle_slow_and_tedious_dma(gc_eaddr_t ea, unsigned char *ls,
266                             unsigned int len, int put_tag)
267 {
268   // Set up for divide and conquer
269   unsigned int alignment = ((uintptr_t) ls) & 0x7;
270   dc_work = make_mask(len) << alignment;
271   dc_ls_base = (unsigned char *) ROUND_DN((uintptr_t) ls, 8);
272   dc_ea_base = ROUND_DN(ea, (gc_eaddr_t) 8);
273   dc_put_tag = put_tag;
274
275   d_and_c( 0, 8);
276   d_and_c( 8, 8);
277   d_and_c(16, 8);
278 }
279
280
281 static void
282 process_job(gc_eaddr_t jd_ea, gc_job_desc_t *jd)
283 {
284   // events: 0x2X
285
286   jd->status = JS_OK;   // assume success
287
288   if (jd->proc_id >= spu_args.nproc_defs)
289     jd->status = JS_UNKNOWN_PROC;
290     
291   else {
292   
293     if (jd->eaa.nargs == 0)
294       (*gc_proc_def[jd->proc_id].proc)(&jd->input, &jd->output, &jd->eaa);
295
296     else {      // handle EA args that must be DMA'd in/out
297
298       gc_job_ea_args_t *eaa = &jd->eaa;
299
300       int NELMS =
301         MAX(MAX_ARGS_EA,
302             (GC_SPU_BUFSIZE + MFC_MAX_DMA_SIZE - 1) / MFC_MAX_DMA_SIZE);
303
304       mfc_list_element_t  dma_get_list[NELMS];
305       //mfc_list_element_t  dma_put_list[NELMS];
306       
307       memset(dma_get_list, 0, sizeof(dma_get_list));
308       //memset(dma_put_list, 0, sizeof(dma_put_list));
309
310       int gli = 0;      // get list index
311       //int pli = 0;    // put list index
312
313       unsigned char *get_base = _gci_getbuf[0];
314       unsigned char *get_t = get_base;
315       unsigned int   total_get_dma_len = 0;
316
317       unsigned char *put_base = _gci_putbuf[pb_idx];
318       unsigned char *put_t = put_base;
319       unsigned int   total_put_alloc = 0;
320       int            put_tag = put_tags + pb_idx;
321
322       // Do we have any "put" args?  If so ensure that previous
323       // dma from this buffer is complete
324
325       gc_log_write2(GCL_SS_SYS, 0x24, put_in_progress, jd->sys.direction_union);
326
327       if ((jd->sys.direction_union & GCJD_DMA_PUT)
328           && (put_in_progress & PBI_MASK(pb_idx))){
329
330         gc_log_write2(GCL_SS_SYS, 0x25, put_in_progress, 1 << put_tag);
331
332         mfc_write_tag_mask(1 << put_tag);       // the tag we're interested in
333         mfc_read_tag_status_all();              // wait for DMA to complete
334         put_in_progress &= ~(PBI_MASK(pb_idx));
335
336         gc_log_write1(GCL_SS_SYS, 0x26, put_in_progress);
337       }
338
339
340       // for now, all EA's must have the same high 32-bits
341       gc_eaddr_t common_ea = eaa->arg[0].ea_addr;
342
343
344       // assign LS addresses for buffers
345       
346       for (unsigned int i = 0; i < eaa->nargs; i++){
347
348         gc_eaddr_t      ea_base = 0;
349         unsigned char  *ls_base;
350         int             offset;
351         unsigned int    dma_len;
352
353         if (eaa->arg[i].direction == GCJD_DMA_GET){
354           ea_base = ROUND_DN(eaa->arg[i].ea_addr, (gc_eaddr_t) CACHE_LINE_SIZE);
355           offset = eaa->arg[i].ea_addr & (CACHE_LINE_SIZE-1);
356           dma_len = ROUND_UP(eaa->arg[i].get_size + offset, CACHE_LINE_SIZE);
357           total_get_dma_len += dma_len;
358
359           if (total_get_dma_len > GC_SPU_BUFSIZE){
360             jd->status = JS_ARGS_TOO_LONG;
361             goto wrap_up;
362           }
363
364           ls_base = get_t;
365           get_t += dma_len;
366           eaa->arg[i].ls_addr = ls_base + offset;
367
368           if (0){
369             assert((mfc_ea2l(eaa->arg[i].ea_addr) & 0x7f) == ((intptr_t)eaa->arg[i].ls_addr & 0x7f));
370             assert((ea_base & 0x7f) == 0);
371             assert(((intptr_t)ls_base & 0x7f) == 0);
372             assert((dma_len & 0x7f) == 0);
373             assert((eaa->arg[i].get_size <= dma_len)
374                    && dma_len <= (eaa->arg[i].get_size + offset + CACHE_LINE_SIZE - 1));
375           }
376
377           // add to dma get list 
378           // FIXME (someday) the dma list is where the JS_BAD_EAH limitation comes from
379
380           while (dma_len != 0){
381             int n = MIN(dma_len, MFC_MAX_DMA_SIZE);
382             dma_get_list[gli].size = n;
383             dma_get_list[gli].eal = mfc_ea2l(ea_base);
384             dma_len -= n;
385             ea_base += n;
386             gli++;
387           }
388         }
389
390         else if (eaa->arg[i].direction == GCJD_DMA_PUT){
391           //
392           // This case is a trickier than the PUT case since we can't
393           // write outside of the bounds of the user provided buffer.
394           // We still align the buffers to 128-bytes for good performance
395           // in the middle portion of the xfers.
396           //
397           ea_base = ROUND_DN(eaa->arg[i].ea_addr, (gc_eaddr_t) CACHE_LINE_SIZE);
398           offset = eaa->arg[i].ea_addr & (CACHE_LINE_SIZE-1);
399
400           uint32_t ls_alloc_len =
401             ROUND_UP(eaa->arg[i].put_size + offset, CACHE_LINE_SIZE);
402
403           total_put_alloc += ls_alloc_len;
404
405           if (total_put_alloc > GC_SPU_BUFSIZE){
406             jd->status = JS_ARGS_TOO_LONG;
407             goto wrap_up;
408           }
409
410           ls_base = put_t;
411           put_t += ls_alloc_len;
412           eaa->arg[i].ls_addr = ls_base + offset;
413
414           if (1){
415             assert((mfc_ea2l(eaa->arg[i].ea_addr) & 0x7f)
416                    == ((intptr_t)eaa->arg[i].ls_addr & 0x7f));
417             assert((ea_base & 0x7f) == 0);
418             assert(((intptr_t)ls_base & 0x7f) == 0);
419           }
420         }
421
422         else
423           assert(0);
424       }
425
426       // fire off the dma to fetch the args and wait for it to complete
427       mfc_getl(get_base, common_ea, dma_get_list, gli*sizeof(dma_get_list[0]), get_tag, 0, 0);
428       mfc_write_tag_mask(1 << get_tag);         // the tag we're interested in
429       mfc_read_tag_status_all();                // wait for DMA to complete
430
431       // do the work
432       (*gc_proc_def[jd->proc_id].proc)(&jd->input, &jd->output, &jd->eaa);
433
434
435       // Do we have any "put" args?  If so copy them out
436       if (jd->sys.direction_union & GCJD_DMA_PUT){
437
438         // Do the copy out using single DMA xfers.  The LS ranges
439         // aren't generally contiguous.
440         
441         bool started_dma = false;
442
443         for (unsigned int i = 0; i < eaa->nargs; i++){
444           if (eaa->arg[i].direction == GCJD_DMA_PUT && eaa->arg[i].put_size != 0){
445             
446             started_dma = true;
447
448             gc_eaddr_t       ea;
449             unsigned char   *ls;
450             unsigned int     len;
451
452             ea = eaa->arg[i].ea_addr;
453             ls = (unsigned char *) eaa->arg[i].ls_addr;
454             len = eaa->arg[i].put_size;
455
456             if (len < 16)
457               handle_slow_and_tedious_dma(ea, ls, len, put_tag);
458             
459             else {
460               if ((ea & 0xf) != 0){
461
462                 // printf("1:  ea = 0x%x  len = %5d\n", (int) ea, len);
463                 
464                 // handle the "pre-multiple-of-16" portion
465                 // do 1, 2, 4, or 8 byte xfers as required
466
467                 if (ea & 0x1){                          // do a 1-byte xfer
468                   mfc_put(ls, ea, 1, put_tag, 0, 0);
469                   ea += 1;
470                   ls += 1;
471                   len -= 1;
472                 }
473                 if (ea & 0x2){                          // do a 2-byte xfer
474                   mfc_put(ls, ea, 2, put_tag, 0, 0);
475                   ea += 2;
476                   ls += 2;
477                   len -= 2;
478                 }
479                 if (ea & 0x4){                          // do a 4-byte xfer
480                   mfc_put(ls, ea, 4, put_tag, 0, 0);
481                   ea += 4;
482                   ls += 4;
483                   len -= 4;
484                 }
485                 if (ea & 0x8){                          // do an 8-byte xfer
486                   mfc_put(ls, ea, 8, put_tag, 0, 0);
487                   ea += 8;
488                   ls += 8;
489                   len -= 8;
490                 }
491               }
492
493               if (1){
494                 // printf("2:  ea = 0x%x  len = %5d\n", (int) ea, len);
495                 assert((ea & 0xf) == 0);
496                 assert((((intptr_t) ls) & 0xf) == 0);
497               }
498
499               // handle the "multiple-of-16" portion
500
501               int aligned_len = ROUND_DN(len, 16);
502               len = len & (16 - 1);
503
504               while (aligned_len != 0){
505                 int dma_len = MIN(aligned_len, MFC_MAX_DMA_SIZE);
506                 mfc_put(ls, ea, dma_len, put_tag, 0, 0);
507                 ea += dma_len;
508                 ls += dma_len;
509                 aligned_len -= dma_len;
510               }
511
512               if (1){
513                 // printf("3:  ea = 0x%x  len = %5d\n", (int)ea, len);
514                 assert((ea & 0xf) == 0);
515                 assert((((intptr_t) ls) & 0xf) == 0);
516               }
517
518               // handle "post-multiple-of-16" portion
519
520               if (len != 0){
521
522                 if (len >= 8){                          // do an 8-byte xfer
523                   mfc_put(ls, ea, 8, put_tag, 0, 0);
524                   ea += 8;
525                   ls += 8;
526                   len -= 8;
527                 }
528                 if (len >= 4){                          // do a 4-byte xfer
529                   mfc_put(ls, ea, 4, put_tag, 0, 0);
530                   ea += 4;
531                   ls += 4;
532                   len -= 4;
533                 }
534                 if (len >= 2){                          // do a 2-byte xfer
535                   mfc_put(ls, ea, 2, put_tag, 0, 0);
536                   ea += 2;
537                   ls += 2;
538                   len -= 2;
539                 }
540                 if (len >= 1){                          // do a 1-byte xfer
541                   mfc_put(ls, ea, 1, put_tag, 0, 0);
542                   ea += 1;
543                   ls += 1;
544                   len -= 1;
545                 }
546                 if (1)
547                   assert(len == 0);
548               }
549             }
550           }
551         }
552         if (started_dma){
553           put_in_progress |= PBI_MASK(pb_idx);          // note it's running
554           gc_log_write2(GCL_SS_SYS, 0x27, put_in_progress, pb_idx);
555           pb_idx ^= 1;                                  // toggle current buffer
556         }
557       }
558     }
559   }
560
561  wrap_up:;      // semicolon creates null statement for C99 compliance
562
563   // Copy job descriptor back out to EA.
564   // (The dma will be waited on in flush_completion_info)
565   int tag = ci_tags + ci_idx;                   // use the current completion tag
566   mfc_put(jd, jd_ea, sizeof(*jd), tag, 0, 0);
567
568   // Tell PPE we're done with the job.
569   //
570   // We queue these up until we run out of room, or until we can send
571   // the info to the PPE w/o blocking.  The blocking check is in
572   // main_loop
573
574   comp_info.job_id[comp_info.ncomplete++] = jd->sys.job_id;
575
576   if (comp_info.ncomplete == GC_CI_NJOBS){
577     gc_log_write0(GCL_SS_SYS, 0x28);
578     flush_completion_info();
579   }
580 }
581
582 static void
583 main_loop(void)
584 {
585   // events: 0x1X
586
587   static gc_job_desc_t  jd;     // static gets us proper alignment
588   gc_eaddr_t            jd_ea;
589   int                   total_jobs = 0;
590
591 #if (USE_LLR_LOST_EVENT)
592   // setup events
593   spu_writech(SPU_WrEventMask, MFC_LLR_LOST_EVENT);
594
595   // prime the pump
596   while (gc_jd_queue_dequeue(spu_args.queue, &jd_ea, ci_tags + ci_idx, &jd))
597     process_job(jd_ea, &jd);
598   // we're now holding a lock-line reservation
599 #endif
600
601   while (1){
602
603 #if (USE_LLR_LOST_EVENT)
604
605     if (unlikely(spu_readchcnt(SPU_RdEventStat))){
606       //
607       // execute standard event handling prologue
608       //
609       int status = spu_readch(SPU_RdEventStat);
610       int mask = spu_readch(SPU_RdEventMask);
611       spu_writech(SPU_WrEventMask, mask & ~status);     // disable active events
612       spu_writech(SPU_WrEventAck, status);              // ack active events
613
614       // execute per-event actions
615
616       if (status & MFC_LLR_LOST_EVENT){
617         //
618         // We've lost a line reservation.  This is most likely caused
619         // by somebody doing something to the queue.  Go look and see
620         // if there's anything for us.
621         //
622         while (gc_jd_queue_dequeue(spu_args.queue, &jd_ea, ci_tags + ci_idx, &jd))
623           process_job(jd_ea, &jd);
624       }
625
626       //
627       // execute standard event handling epilogue
628       //
629       spu_writech(SPU_WrEventMask, mask);       // restore event mask
630     }
631
632 #else
633
634     // try to get a job from the job queue 
635     if (gc_jd_queue_dequeue(spu_args.queue, &jd_ea, ci_tags + ci_idx, &jd)){
636       total_jobs++;
637       gc_log_write2(GCL_SS_SYS, 0x10, jd.sys.job_id, total_jobs);
638
639       process_job(jd_ea, &jd); 
640
641       gc_log_write2(GCL_SS_SYS, 0x11, jd.sys.job_id, total_jobs);
642       backoff_reset(); 
643     }
644     else
645       backoff_delay();
646
647 #endif
648
649     // any msgs for us?
650
651     if (unlikely(spu_readchcnt(SPU_RdInMbox))){
652       int msg = spu_readch(SPU_RdInMbox);
653       // printf("spu[%d] mbox_msg: 0x%08x\n", spu_args.spu_idx, msg);
654       if (MBOX_MSG_OP(msg) == OP_EXIT){
655         flush_completion_info();
656         return;
657       }
658       if (MBOX_MSG_OP(msg) == OP_GET_SPU_BUFSIZE){
659         spu_writech(SPU_WrOutIntrMbox, MK_MBOX_MSG(OP_SPU_BUFSIZE, GC_SPU_BUFSIZE_BASE));
660       }
661     }
662
663     // If we've got job completion info for the PPE and we can send a
664     // message without blocking, do it.
665
666     if (comp_info.ncomplete != 0 && spu_readchcnt(SPU_WrOutIntrMbox) != 0){
667       gc_log_write0(GCL_SS_SYS, 0x12);
668       flush_completion_info();
669     }
670   }
671 }
672
673
674 int
675 main(unsigned long long spe_id __attribute__((unused)),
676      unsigned long long argp,
677      unsigned long long envp __attribute__((unused)))
678 {
679   gc_sys_tag = mfc_tag_reserve();       // allocate a tag for our misc DMA operations
680   get_tag  = mfc_tag_reserve();
681   ci_tags  = mfc_multi_tag_reserve(2);
682   put_tags = mfc_multi_tag_reserve(2);
683
684 #if 0  
685   printf("gc_sys_tag = %d\n", gc_sys_tag);
686   printf("get_tag    = %d\n", get_tag);
687   printf("ci_tags    = %d\n", ci_tags);
688   printf("put_tags   = %d\n", put_tags);
689 #endif
690
691   // dma the args in
692   mfc_get(&spu_args, argp, sizeof(spu_args), gc_sys_tag, 0, 0);
693   mfc_write_tag_mask(1 << gc_sys_tag);  // the tag we're interested in
694   mfc_read_tag_status_all();            // wait for DMA to complete
695
696   // initialize pointer to procedure entry table
697   gc_proc_def = (gc_proc_def_t *) spu_args.proc_def_ls_addr;
698
699   gc_set_seed(spu_args.spu_idx);
700
701   // initialize logging
702   _gc_log_init(spu_args.log);
703
704   backoff_init();               // initialize backoff parameters
705
706   main_loop();
707   return 0;
708 }