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