source: trunk/softs/giet_tsar/drivers.c @ 175

Last change on this file since 175 was 173, checked in by alain, 13 years ago

_barrier_wait(): separate count and lock variables

File size: 41.2 KB
Line 
1/****************************************************************************************
2File : drivers.c
3Written by Alain Greiner & Nicolas Pouillon
4Date : december 2010
5
6Basic drivers used by the GIET, that is running
7on the MIPS32 processor architecture.
8
9The supported peripherals are:
10- the SoClib pibus_multi_tty
11- the SocLib pibus_timer
12- the SocLib pibus_dma
13- The SoCLib pibus_icu
14- The SoCLib pibus_gcd
15- The SoCLib pibus_frame_buffer
16- The SoCLib pibus_block_device
17
18The following global parameters must be defined in the ldscript.
19- NB_CLUSTERS : number of clusters
20- NB_PROCS : number of processor per cluster
21- NB_NTASKS : max number of tasks per processor
22- NB_LOCKS : max number of supported spin_locks
23- NB_TIMERS : max number of timers per processor
24
25The follobing base addresses must be defined in the ldscript
26- seg_icu_base
27- seg_timer_base
28- seg_tty_base
29- seg_gcd_base
30- seg_dma_base
31- seg_locks_base
32- seg_fb_base
33- seg_ioc_base
34****************************************************************************************/
35
36#include "drivers.h"
37#include "icu.h"
38#include "block_device.h"
39#include "dma.h"
40
41struct plouf;
42
43//////////////////////////////////////////////////////////////
44// various informations that must be defined in ldscript
45//////////////////////////////////////////////////////////////
46extern struct plouf seg_icu_base;
47extern struct plouf seg_timer_base;
48extern struct plouf seg_tty_base;
49extern struct plouf seg_gcd_base;
50extern struct plouf seg_dma_base;
51extern struct plouf seg_locks_base;
52extern struct plouf seg_fb_base;
53extern struct plouf seg_ioc_base;
54
55extern struct plouf NB_CLUSTERS;
56extern struct plouf NB_PROCS;
57extern struct plouf NB_TASKS;
58extern struct plouf NB_TIMERS;
59extern struct plouf NB_LOCKS;
60
61#define in_drivers __attribute__((section (".drivers")))
62#define in_unckdata __attribute__((section (".unckdata")))
63
64////////////////////////////////////////////////////////////////////////////////////////
65//  Global uncachable variables for synchronization between drivers and ISRs
66////////////////////////////////////////////////////////////////////////////////////////
67
68in_unckdata int volatile    _dma_status[256];
69in_unckdata int volatile    _dma_busy[256]   =   { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
70                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
71                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
72                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
73                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
74                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
75                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
76                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
77                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
78                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
79                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
80                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
81                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
83                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
84                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
85
86in_unckdata int volatile    _ioc_lock    = 0;
87in_unckdata int volatile    _ioc_done    = 0;
88in_unckdata int volatile    _ioc_status;
89
90in_unckdata char volatile   _tty_get_buf[256];
91in_unckdata int volatile    _tty_get_full[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
92                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
93                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
94                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
95                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
96                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
97                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
98                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
99                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
100                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
101                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
102                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
103                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
104                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
105                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
106                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
107
108in_unckdata char volatile   _tty_put_buf[256];
109in_unckdata int volatile    _tty_put_full[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
110                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
111                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
112                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
113                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
114                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
115                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
116                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
117                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
118                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
119                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
120                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
121                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
122                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
123                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
124                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
125
126////////////////////////////////////////////////////////////////////////////////////////
127//  Global uncachable variables for inter-task barriers
128////////////////////////////////////////////////////////////////////////////////////////
129
130in_unckdata int volatile    _barrier_initial_value[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
131in_unckdata int volatile    _barrier_count[16]         = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
132in_unckdata int volatile    _barrier_lock[16]          = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
133
134////////////////////////////////////////////////////////////////////////////////////////
135//  Global uncachable variables for spin_locks using LL/C instructions
136////////////////////////////////////////////////////////////////////////////////////////
137
138in_unckdata int volatile    _spin_lock[256] =    { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
139                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
140                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
141                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
142                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
143                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
144                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
145                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
146                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
147                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
148                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
149                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
150                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
151                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
152                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
153                                                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
154
155////////////////////////////////////////////////////////////////////////////////////////
156//  mempcy()
157// GCC requires this function. Taken from MutekH.
158////////////////////////////////////////////////////////////////////////////////////////
159__attribute((used))
160in_drivers static void *memcpy(void *_dst, const void *_src, unsigned int size)
161{
162    unsigned int *dst = _dst;
163    const unsigned int *src = _src;
164    if ( ! ((unsigned int)dst & 3) && ! ((unsigned int)src & 3) )
165        while (size > 3) {
166            *dst++ = *src++;
167            size -= 4;
168        }
169
170    unsigned char *cdst = (unsigned char*)dst;
171    unsigned char *csrc = (unsigned char*)src;
172
173    while (size--) {
174        *cdst++ = *csrc++;
175    }
176    return _dst;
177}
178
179////////////////////////////////////////////////////////////////////////////////////////
180//  _procid()
181// Access CP0 and returns processor ident
182// No more than 1024 processors...
183////////////////////////////////////////////////////////////////////////////////////////
184in_drivers unsigned int _procid()
185{
186    unsigned int ret;
187    asm volatile( "mfc0 %0, $15, 1": "=r"(ret) );
188    return (ret & 0x3FF);
189}
190////////////////////////////////////////////////////////////////////////////////////////
191//  _segment_increment()
192// Access CP0 to get the procid, and returns the address increment to access
193// various peripherals (TTY, TIMER, ICU, DMA), in case of multiprocessors architectures.
194// It uses the NB_PROCS and NB_CLUSTERS parameters to compute this increment:
195// - increment  = cluster_id*cluster_increment + local_id*local_increment
196// - cluster_id = procid / NB_PROCS 
197// - local_id   = procid % NB_PROCS
198// - cluster_increment = 4G / NB_CLUSTERS
199////////////////////////////////////////////////////////////////////////////////////////
200in_drivers unsigned int _segment_increment(unsigned int local_increment)
201{
202    unsigned int        nprocs                  = (unsigned int)&NB_PROCS;
203    unsigned int        nclusters               = (unsigned int)&NB_CLUSTERS;
204    unsigned int        cluster_increment       = (0x80000000/nclusters)*2;
205    unsigned int        pid                     = _procid();
206    return (pid / nprocs)*cluster_increment + (pid % nprocs)*local_increment;
207}
208////////////////////////////////////////////////////////////////////////////////////////
209//  _proctime()
210// Access CP0 and returns processor time
211////////////////////////////////////////////////////////////////////////////////////////
212in_drivers unsigned int _proctime()
213{
214    unsigned int ret;
215    asm volatile( "mfc0 %0, $9": "=r"(ret) );
216    return ret;
217}
218////////////////////////////////////////////////////////////////////////////////////////
219//  _procnumber()
220// Returns the number of processsors controled by the GIET
221////////////////////////////////////////////////////////////////////////////////////////
222in_drivers unsigned int _procnumber()
223{
224    return (unsigned int)&NB_PROCS * (unsigned int)&NB_CLUSTERS;
225}
226////////////////////////////////////////////////////////////////////////////////////////
227//  _it_mask()
228// Access CP0 and mask IRQs
229////////////////////////////////////////////////////////////////////////////////////////
230in_drivers void _it_mask()
231{
232    int tmp;
233    asm volatile("mfc0  %0, $12"    : "=r" (tmp) );
234    asm volatile("ori   %0, %0, 1"  : "=r" (tmp) );
235    asm volatile("mtc0  %0, $12"    : "=r" (tmp) );
236}
237////////////////////////////////////////////////////////////////////////////////////////
238//  _it_enable()
239// Access CP0 and enable IRQs
240////////////////////////////////////////////////////////////////////////////////////////
241in_drivers void _it_enable()
242{
243    int tmp;
244    asm volatile("mfc0  %0, $12"    : "=r" (tmp) );
245    asm volatile("addi  %0, %0, -1" : "=r" (tmp) );
246    asm volatile("mtc0  %0, $12"    : "=r" (tmp) );
247}
248//////////////////////////////////////////////////////////////////////
249//  _dcache_buf_invalidate()
250// Invalidate all cache lines corresponding to a memory buffer.
251// This is used by the block_device driver.
252/////////////////////////////////////////////////////////////////////////
253in_drivers void _dcache_buf_invalidate(const void * buffer, size_t size)
254{
255    size_t i;
256    size_t dcache_line_size;
257
258    // retrieve dcache line size from config register (bits 12:10)
259    asm volatile("mfc0 %0, $16, 1" : "=r" (dcache_line_size));
260
261    dcache_line_size = 2 << ((dcache_line_size>>10) & 0x7);
262
263    // iterate on lines to invalidate each one of them
264    for ( i=0; i<size; i+=dcache_line_size )
265        asm volatile(" cache %0, %1"
266                :
267                :"i" (0x11), "R" (*((char*)buffer+i)));
268}
269
270/////////////////////////////////////////////////////////////////////////
271//  _itoa_dec()
272// convert a 32 bits unsigned int to a string of 10 decimal characters.
273/////////////////////////////////////////////////////////////////////////
274in_drivers void _itoa_dec(unsigned val, char* buf)
275{
276    const char  DecTab[] = "0123456789";
277    unsigned int i;
278    for( i=0 ; i<10 ; i++ )
279    {
280        if( (val!=0) || (i==0) ) buf[9-i] = DecTab[val % 10];
281        else                     buf[9-i] = 0x20;
282        val /= 10;
283    }
284}
285//////////////////////////////////////////////////////////////////////////
286//  _itoa_hex()
287// convert a 32 bits unsigned int to a string of 8 hexadecimal characters.
288///////////////////////////////////////////////////////////////////////////
289in_drivers void _itoa_hex(int val, char* buf)
290{
291    const char  HexaTab[] = "0123456789ABCD";
292    unsigned int i;
293    for( i=0 ; i<8 ; i++ )
294    {
295        buf[7-i] = HexaTab[val % 16];
296        val /= 16;
297    }
298}
299///////////////////////////////////////////////////////////////////////////////////////
300// MULTI_TIMER component
301// Each processor can handle up to NB_TIMERS independant timers.
302// The segment base address is defined as
303//         seg_timer_base + segment_increment(NB_TIMERS*16) + index*16
304///////////////////////////////////////////////////////////////////////////////////////
305//  _timer_write()
306// Write a 32 bits word in a memory mapped register of the MULTI_TIMER
307///////////////////////////////////////////////////////////////////////////////////////
308in_drivers int _timer_write(size_t timer_index, size_t register_index, int value)
309{
310    int*                timer_address;
311    size_t              ntimers         = (size_t)&NB_TIMERS;
312    unsigned int        base            = (unsigned int)&seg_timer_base;
313    unsigned int        increment       = _segment_increment(ntimers*TIMER_SPAN*4); 
314
315    if( timer_index >= ntimers)         return -1;
316    if( register_index >= TIMER_SPAN )  return -1;
317
318    timer_address = (int*)(base + increment + timer_index*TIMER_SPAN*4);
319    timer_address[register_index] = value;          // write word
320    return 0;
321}
322///////////////////////////////////////////////////////////////////////////////////////
323//  _timer_read()
324// Read a 32 bits word in a memory mapped register of the MULTI_TIMER
325///////////////////////////////////////////////////////////////////////////////////////
326in_drivers int _timer_read(size_t timer_index, size_t register_index, int* buffer)
327{
328    int*                timer_address;
329    size_t              ntimers         = (size_t)&NB_TIMERS;
330    unsigned int        base            = (unsigned int)&seg_timer_base;
331    unsigned int        increment       = _segment_increment(ntimers*TIMER_SPAN*4); 
332
333    if( timer_index >= ntimers)         return -1;
334    if( register_index >= TIMER_SPAN )  return -1;
335
336    if( timer_index >= ntimers) return -1;
337    if( register_index >= TIMER_SPAN ) return -1;
338
339    timer_address = (int*)(base + increment + timer_index*TIMER_SPAN*4);
340    *buffer = timer_address[register_index];        // read word
341    return 0;
342}
343///////////////////////////////////////////////////////////////////////////////////////
344//  MULTI_TTY COMPONENT
345// The total number of TTYs is equal to NB_CLUSTERS * NB_PROCS * NB_TASKS.
346// - tty_address = seg_tty_base + _segment_increment(NB_TASKS*16) + task_id*16
347// - tty_index   = proc_id*NB_TASKS + task_id
348///////////////////////////////////////////////////////////////////////////////////////
349//  _tty_write()
350// Write one or several characters directly from a fixed length user buffer
351// to the TTY_WRITE register of the TTY controler.
352// It doesn't use the TTY_PUT_IRQ interrupt and the associated kernel buffer.
353// This is a non blocking call : it test the TTY_STATUS register.
354// If the TTY_STATUS_WRITE bit is set, the transfer stops and the function
355// returns  the number of characters that have been actually written.
356// It returns -1 in case of error (proc_id or task index too large)
357///////////////////////////////////////////////////////////////////////////////////////
358in_drivers int _tty_write(char* buffer, int length)
359{
360    char*               tty_address;
361    size_t              ntasks          = (size_t)&NB_TASKS;
362    size_t              nprocs          = (size_t)&NB_PROCS;
363    size_t                  nclusters   = (size_t)&NB_CLUSTERS;
364    unsigned int    base                = (unsigned int)&seg_tty_base;
365    unsigned int        increment       = _segment_increment(ntasks*TTY_SPAN*4);
366    size_t              pid             = _procid();
367    int                 nwritten        = 0;
368    size_t                  tid;
369    int                 i;
370
371    if( ntasks == 0 )  tid = 0;
372    else               tid = _current_task_array[pid];
373
374    if( tid >= ntasks )                         return -1;
375    if( pid >= nprocs*nclusters )       return -1;
376
377    tty_address = (char*)(base + increment + tid*TTY_SPAN*4);
378
379    for ( i=0 ; i < length ; i++ )
380    {
381        if((tty_address[TTY_STATUS*4] & 0x2) == 0x2)  break;
382        else
383        {
384            tty_address[TTY_WRITE*4] = buffer[i]; // write character
385            nwritten++;
386        }
387    }
388    return nwritten;
389}
390///////////////////////////////////////////////////////////////////////////////////////
391//  _tty_read()
392// Fetch one character directly from the TTY_READ register of the TTY controler,
393// and writes this character to the user buffer.
394// It doesn't use the TTY_GET_IRQ interrupt and the associated kernel buffer.
395// This is a non blocking call : it returns 0 if the register is empty,
396// and returns 1 if the register is full.
397// It returns -1 in case of error (proc_id or task_id too large or length != 1)
398// The length argument is not used in this implementation, and has been
399// introduced for future implementations.
400///////////////////////////////////////////////////////////////////////////////////////
401in_drivers int _tty_read(char* buffer, int length)
402{
403    char*               tty_address;
404    size_t              ntasks          = (size_t)&NB_TASKS;
405    size_t              nprocs          = (size_t)&NB_PROCS;
406    size_t              nclusters       = (size_t)&NB_CLUSTERS;
407    unsigned int    base                = (unsigned int)&seg_tty_base;
408    unsigned int        increment       = _segment_increment(ntasks*TTY_SPAN*4);
409    size_t              pid             = _procid();
410    size_t                  tid;
411
412    if( pid > 7 )   tid = 0;
413    else            tid = _current_task_array[pid];
414
415    if( length != 1)                        return -1;
416    if( pid >= nprocs*nclusters )   return -1;
417    if( tid >= ntasks )                     return -1;
418   
419    tty_address = (char*)(base + increment + tid*TTY_SPAN*4);
420
421    if((tty_address[TTY_STATUS*4] & 0x1) == 0x1)
422    {
423        buffer[0] = tty_address[TTY_READ*4];
424        return 1;
425    }
426    else
427    {
428        return 0;
429    }
430}
431///////////////////////////////////////////////////////////////////////////////////////
432//  _tty_read_irq()
433// iAS it uses the TTY_GET_IRQ interrupt and the associated kernel buffer,
434// that has been written by the ISR, this function does not access the TTY registers.
435// It fetch one single character from the _tty_get_buf[tty_index] kernel buffer, writes
436// this character to the user buffer, and reset the _tty_get_full[tty_index] buffer.
437// This is a non blocking call : it returns 0 if the kernel buffer is empty,
438// and returns 1 if the buffer is full.
439// It returns -1 in case of error (proc_id or task_id too large, or length != 1)
440// The length argument is not used in this implementation, and has been
441// introduced for future implementations.
442///////////////////////////////////////////////////////////////////////////////////////
443in_drivers int _tty_read_irq(char* buffer, int length)
444{
445    int     pid         = _procid();
446    int     ntasks      = (int)&NB_TASKS;
447    int     nprocs      = (int)&NB_PROCS;
448    int     nclusters   = (int)&NB_CLUSTERS;
449    int     tty_index;
450    int     tid;
451
452    if( pid > 7 )   tid = 0;
453    else            tid = _current_task_array[pid];
454 
455    if( length != 1)                    return -1;
456    if( pid >= nprocs*nclusters )       return -1;
457    if( tid >= ntasks )                 return -1;
458
459    tty_index = pid*ntasks + tid;
460    if( _tty_get_full[tty_index] == 0 ) return 0;
461
462    *buffer = _tty_get_buf[tty_index];
463    _tty_get_full[tty_index] = 0;
464    return 1;
465}
466///////////////////////////////////////////////////////////////////////////////////////
467//  _exit()
468// Exit (suicide) after printing message on  a TTY terminal.
469///////////////////////////////////////////////////////////////////////////////////////
470in_drivers int  _exit()
471{
472    char buf[] = "\n\n!!!  Exit  Processor          !!!\n";
473    int pid = _procid();
474
475    buf[24] = '0';
476    buf[25] = 'x';
477    buf[26] = (char)((pid>>8) & 0xF) + 0x30;
478    buf[27] = (char)((pid>>4) & 0xF) + 0x30;
479    buf[28] = (char)(pid & 0xF)      + 0x30;
480    _tty_write(buf, 36);
481
482    while(1) asm volatile("nop");   // infinite loop...
483}
484
485///////////////////////////////////////////////////////////////////////////////////////
486//  _icu_write()
487// Write a 32 bits word in a memory mapped register of the ICU peripheral
488// The base address is defined by the processor ID
489///////////////////////////////////////////////////////////////////////////////////////
490in_drivers int _icu_write(size_t register_index, int value)
491{
492    int*                icu_address;
493    unsigned int        base = (int)&seg_icu_base;
494    unsigned int        increment = _segment_increment(ICU_SPAN*4);
495
496    if( register_index >= ICU_SPAN ) return -1;
497
498    icu_address = (int*)(base + increment);
499    icu_address[register_index] = value;   // write word
500    return 0;
501}
502///////////////////////////////////////////////////////////////////////////////////////
503//  _icu_read()
504// Read a 32 bits word in a memory mapped register of the ICU peripheral
505// The ICU base address is defined by the processor ID
506///////////////////////////////////////////////////////////////////////////////////////
507in_drivers int _icu_read(size_t register_index, int* buffer)
508{
509    int*                icu_address;
510    unsigned int        base = (int)&seg_icu_base;
511    unsigned int        increment = _segment_increment(ICU_SPAN*4);
512
513    if( register_index >= ICU_SPAN ) return -1;
514
515    icu_address = (int*)(base + increment);
516    *buffer = icu_address[register_index];      // read word
517    return 0;
518}
519///////////////////////////////////////////////////////////////////////////////////////
520//  _gcd_write()
521// Write a 32 bits word in a memory mapped register of the GCD coprocessor
522///////////////////////////////////////////////////////////////////////////////////////
523in_drivers int _gcd_write(size_t register_index, int value)
524{
525    int*    gcd_address;
526    if( register_index >= 4 ) return -1;
527
528    gcd_address = (int*)&seg_gcd_base;
529    gcd_address[register_index] = value;            // write word
530    return 0;
531}
532///////////////////////////////////////////////////////////////////////////////////////
533//  _gcd_read()
534// Read a 32 bits word in a memory mapped register of the GCD coprocessor
535///////////////////////////////////////////////////////////////////////////////////////
536in_drivers int _gcd_read(size_t register_index, int* buffer)
537{
538    int*    gcd_address;
539    if( register_index >= 4 ) return -1;
540
541    gcd_address = (int*)&seg_gcd_base;
542    *buffer = gcd_address[register_index];          // read word
543    return 0;
544}
545///////////////////////////////////////////////////////////////////////////////////////
546//  _locks_write()
547// Release a software spin-lock
548///////////////////////////////////////////////////////////////////////////////////////
549in_drivers int _locks_write(size_t index)
550
551{
552    int     max = (int)&NB_LOCKS;
553    if( index >= max ) return -1;
554
555    _spin_lock[index] = 0;
556    return 0;
557}
558///////////////////////////////////////////////////////////////////////////////////////
559//  _locks_read()
560// Try to take a software spin-lock.
561// This is a blocking call, as there is a busy-waiting loop,
562// until the lock is granted to the requester.
563// There is an internal delay of about 100 cycles between
564// two successive lock read, to avoid bus saturation.
565///////////////////////////////////////////////////////////////////////////////////////
566in_drivers int _locks_read(size_t index)
567{
568    int     max = (int)&NB_LOCKS;
569    if( index >= max ) return -1;
570
571    register int        delay = ( (_proctime() + _procid() ) & 0xF) << 4;
572    register int*       plock = (int*)&_spin_lock[index];                       
573
574    asm volatile ("_locks_llsc:                 \n"
575                  "ll   $2,    0(%0)            \n"     // $2 <= _locks_lock
576                  "bnez $2,    _locks_delay     \n"     // random delay if busy
577                  "li   $3,    1                \n"     // prepare argument for sc 
578                  "sc   $3,    0(%0)            \n"     // try to set _locks_busy
579                  "bnez $3,    _locks_ok        \n"     // exit if atomic
580                  "_locks_delay:                \n"
581                  "move $4,    %1               \n"     // $4 <= delay
582                  "_locks_loop:                 \n"
583                  "addi $4,    $4,    -1        \n"     // $4 <= $4 - 1
584                  "beqz $4,    _locks_loop      \n"     // test end delay
585                  "j           _locks_llsc      \n"     // retry
586                  "_locks_ok:                   \n"
587                  ::"r"(plock),"r"(delay):"$2","$3","$4");
588    return 0;
589}
590//////////////////////////////////////////////////////////////////////////////////////////
591//  I/O BLOCK_DEVICE
592// The three functions below use the three variables _ioc_lock _ioc_done,
593// and _ioc_status for synchronsation.
594// - As the IOC component can be used by several programs running in parallel,
595// the _ioc_lock variable guaranties exclusive access to the device.
596// The _ioc_read() and _ioc_write() functions use atomic LL/SC to get the lock.
597// and set _ioc_lock to a non zero value.
598// The _ioc_write() and _ioc_read() functions are blocking, polling the _ioc_lock
599// variable until the device is available.
600// - When the tranfer is completed, the ISR routine activated by the IOC IRQ
601// set the _ioc_done variable to a non-zero value. Possible address errors detected
602// by the IOC peripheral are reported by the ISR in the _ioc_status variable.
603// The _ioc_completed() function is polling the _ioc_done variable, waiting for
604// tranfer conpletion. When the completion is signaled, the _ioc_completed() function
605// reset the _ioc_done variable to zero, and releases the _ioc_lock variable.
606//
607// In a multi-tasks environment, this polling policy must be replaced by a
608// descheduling policy for the requesting process.
609///////////////////////////////////////////////////////////////////////////////////////
610//  _ioc_get_lock()
611// This blocking function is used by the _ioc_read() and _ioc_write() functions
612// to get _ioc_lock using LL/SC.
613///////////////////////////////////////////////////////////////////////////////////////
614in_drivers void _ioc_get_lock()
615{
616    register unsigned int       delay = (_proctime() & 0xF) << 4;
617    register unsigned int*      plock = (unsigned int*)&_ioc_lock;                     
618
619    asm volatile ("_ioc_llsc:                           \n"
620                  "ll   $2,    0(%0)                \n" // $2 <= _ioc_lock
621                  "bnez $2,    _ioc_delay           \n" // random delay if busy
622                  "li   $3,    1                            \n" // prepare argument for sc 
623                  "sc   $3,    0(%0)                \n" // try to set _ioc_busy
624                  "bnez $3,    _ioc_ok              \n" // exit if atomic
625                  "_ioc_delay:                              \n"
626                  "move $4,    %1                           \n" // $4 <= delay
627                  "_ioc_loop:                               \n"
628                  "addi $4,    $4,    -1            \n" // $4 <= $4 - 1
629                  "beqz $4,    _ioc_loop            \n" // test end delay
630                  "j           _ioc_llsc        \n"     // retry
631                  "_ioc_ok:                                 \n"
632                  ::"r"(plock),"r"(delay):"$2","$3","$4");
633}
634//////////////////////////////////////////////////////////////////////////////////////
635//  _ioc_write()
636// Transfer data from a memory buffer to a file on the block_device.
637// - lba    : first block index on the disk
638// - buffer : base address of the memory buffer
639// - count  : number of blocks to be transfered
640// The source buffer must be in user address space.
641///////////////////////////////////////////////////////////////////////////////////////
642in_drivers int _ioc_write(size_t lba, void* buffer, size_t count)
643{
644    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
645
646    // buffer must be in user space
647//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
648//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
649//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
650
651    // get the lock
652    _ioc_get_lock();
653
654    // block_device configuration
655    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
656    ioc_address[BLOCK_DEVICE_COUNT] = count;
657    ioc_address[BLOCK_DEVICE_LBA] = lba;
658    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
659    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
660    return 0;
661}
662///////////////////////////////////////////////////////////////////////////////////////
663//  _ioc_read()
664// Transfer data from a file on the block device to a memory buffer.
665// - lba    : first block index on the disk
666// - buffer : base address of the memory buffer
667// - count  : number of blocks to be transfered
668// The destination buffer must be in user address space.
669// All cache lines corresponding to the the target buffer must be invalidated
670// for cache coherence.
671///////////////////////////////////////////////////////////////////////////////////////
672in_drivers int _ioc_read(size_t lba, void* buffer, size_t count)
673{
674    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
675
676    // buffer must be in user space
677//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
678//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
679//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
680
681    // get the lock
682    _ioc_get_lock();
683
684    // block_device configuration
685    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
686    ioc_address[BLOCK_DEVICE_COUNT] = count;
687    ioc_address[BLOCK_DEVICE_LBA] = lba;
688    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
689    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
690
691    return 0;
692}
693///////////////////////////////////////////////////////////////////////////////////////
694//  _ioc_completed()
695// This blocking function cheks completion of an I/O transfer and reports errors.
696// It returns 0 if the transfer is successfully completed.
697// It returns -1 if an error has been reported.
698///////////////////////////////////////////////////////////////////////////////////////
699in_drivers int _ioc_completed()
700{
701    // waiting for completion
702    while (_ioc_done == 0) { asm volatile("nop"); }
703   
704    // reset synchronisation variables
705    _ioc_done = 0;
706    _ioc_lock = 0;
707
708    // return errors
709    if((_ioc_status != BLOCK_DEVICE_READ_SUCCESS) &&
710            (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS))    return -1;
711    else                                                                    return 0;
712}
713
714//////////////////////////////////////////////////////////////////////////////////////
715//  FRAME_BUFFER
716// The _fb_sync_write & _fb_sync_read functions use a memcpy strategy to implement
717// the transfer between a data buffer (user space) and the frame buffer (kernel space).
718// They are blocking until completion of the transfer.
719//////////////////////////////////////////////////////////////////////////////////////
720//  _fb_sync_write()
721// Transfer data from an user buffer to the frame_buffer device with a memcpy.
722// - offset     : offset (in bytes) in the frame buffer
723// - buffer : base address of the memory buffer
724// - length : number of bytes to be transfered
725//////////////////////////////////////////////////////////////////////////////////////
726in_drivers int  _fb_sync_write(size_t offset, void* buffer, size_t length)
727{
728    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
729    char*       ub = buffer;
730    size_t      i;
731
732    // buffer must be in user space
733//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
734//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
735
736    // memory copy
737    for(i=0 ; i<length ; i++) fb[i] = ub[i];
738    return 0;
739}
740///////////////////////////////////////////////////////////////////////////////////////
741//  _fb_sync_read()
742// Transfer data from the frame_buffer device to an user buffer with a memcpy.
743// - offset     : offset (in bytes) in the frame buffer
744// - buffer : base address of the memory buffer
745// - length : number of bytes to be transfered
746//////////////////////////////////////////////////////////////////////////////////////
747in_drivers int  _fb_sync_read(size_t offset, void* buffer, size_t length)
748{
749    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
750    char*       ub = buffer;
751    size_t      i;
752
753    // buffer must be in user space
754//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
755//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
756
757    // memory copy
758    for(i=0 ; i<length ; i++) ub[i] = fb[i];
759    return 0;
760}
761//////////////////////////////////////////////////////////////////////////////////////
762// The _fb_write() and _fb_read() functions use the MULTI_DMA
763// coprocessor to transfer data between the user buffer and the frame buffer.
764// The _fb_completed() function, use a polling policy to test
765// the global variables _dma_busy[i] and detect the transfer completion.
766// As each processor can have it's private DMA, there is up to 256 _dma_busy[i]
767// set/reset variables that are indexed by the proc_id.
768// The _dma_busy variable is reset by the ISR associated to the DMA IRQ.
769///////////////////////////////////////////////////////////////////////////////////////
770//  _fb_write()
771// Transfer data from an user buffer to the frame_buffer device using DMA.
772// - offset : offset (in bytes) in the frame buffer
773// - buffer : base address of the memory buffer
774// - length : number of bytes to be transfered
775//////////////////////////////////////////////////////////////////////////////////////
776in_drivers int  _fb_write(size_t offset, void* buffer, size_t length)
777{
778    int*                dma_address;
779    unsigned int        base            = (unsigned int)&seg_dma_base;
780    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
781    char*               fb              = (char*)&seg_fb_base + offset;
782    unsigned int        delay           = (_proctime() & 0xF) << 4;
783    unsigned int        pid             = _procid();
784    unsigned int        i;
785
786
787    // checking buffer boundaries (bytes)
788//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
789//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
790
791    // waiting until DMA device is available
792    while (_dma_busy[pid] != 0)
793    {
794        for( i=0 ; i<delay ; i++)   // busy waiting
795        {                           // with a pseudo random
796            asm volatile("nop");    // delay between bus accesses
797        }
798    }
799    _dma_busy[pid] = 1;
800
801    dma_address = (int*)(base + increment);
802 
803    // DMA configuration
804    dma_address[DMA_IRQ_DISABLE] = 0;
805    dma_address[DMA_SRC]        = (int)buffer;
806    dma_address[DMA_DST]        = (int)fb;
807    dma_address[DMA_LEN]        = (int)length;
808    return 0;
809}
810///////////////////////////////////////////////////////////////////////////////////////
811//  _fb_read()
812// Transfer data from the frame_buffer device to an user buffer using DMA.
813// - offset     : offset (in bytes) in the frame buffer
814// - buffer : base address of the memory buffer
815// - length : number of bytes to be transfered
816//////////////////////////////////////////////////////////////////////////////////////
817in_drivers int  _fb_read(size_t offset, void* buffer, size_t length)
818{
819    int*                dma_address;
820    unsigned int        base            = (unsigned int)&seg_dma_base; 
821    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
822    char*               fb              = (char*)&seg_fb_base + offset;
823    unsigned int        delay           = (_proctime() & 0xF) << 4;
824    unsigned int        pid             = _procid();
825    unsigned int        i;
826
827    // checking buffer boundaries (bytes)
828//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
829//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
830
831    // waiting until DMA device is available
832    while (_dma_busy[pid] != 0)
833    {
834        for( i=0 ; i<delay ; i++)   // busy waiting
835        {                           // with a pseudo random
836            asm volatile("nop");    // delay between bus accesses
837        }
838    }
839    _dma_busy[pid] = 1;
840
841    dma_address = (int*)(base + increment);
842
843    // DMA configuration
844    dma_address[DMA_IRQ_DISABLE] = 0;
845    dma_address[DMA_SRC]        = (int)fb;
846    dma_address[DMA_DST]        = (int)buffer;
847    dma_address[DMA_LEN]        = (int)length;
848    return 0;
849}
850///////////////////////////////////////////////////////////////////////////////////////
851//  _fb_completed()
852// This blocking function cheks completion of a DMA transfer to or fom the frame buffer.
853// The MIPS32 wait instruction stall the processor until the next interrupt.
854// It returns 0 if the transfer is successfully completed
855// It returns -1 if an error has been reported.
856///////////////////////////////////////////////////////////////////////////////////////
857in_drivers int _fb_completed()
858{
859    unsigned int        pid = _procid();
860
861    while (_dma_busy[pid] != 0)
862    {
863        asm volatile("nop");
864    }
865    if(_dma_status[pid] == DMA_SUCCESS)  return 0;
866    else                                 return _dma_status[pid];
867}
868//////////////////////////////////////////////////////////////////////////////////////
869// _barrier_init()
870// This function makes a cooperative initialisation of the barrier:
871// - barrier_count[index] <= N
872// - barrier_lock[index]  <= 0
873// All tasks try to initialize the barrier, but the initialisation
874// is done by only one task, using LL/SC instructions.
875// This cooperative initialisation is questionnable,
876// bcause the barrier can ony be initialised once...
877//////////////////////////////////////////////////////////////////////////////////////
878in_drivers int _barrier_init(unsigned int index, unsigned int value)
879{
880
881    register int* pinit         = (int*)&_barrier_initial_value[index];
882    register int* pcount        = (int*)&_barrier_count[index];
883    register int* plock         = (int*)&_barrier_lock[index];
884
885    if ( index > 7 )    return 1;
886
887    // parallel initialisation using atomic instructions LL/SC
888    asm volatile ("_barrier_init_test:                  \n"
889                  "ll   $2,     0(%0)                   \n"     // read barrier_inital_value
890                  "bnez $2,     _barrier_init_done      \n"
891                  "move $3,     %3                              \n"
892                  "sc   $3,     0(%0)                   \n"     // try to write barrier_initial_value
893                  "beqz $3,     _barrier_init_test      \n"
894                  "move $3,     %3                                  \n" 
895                  "sw   $3,     0(%1)                           \n"     // barrier_count <= barrier_initial_value
896                  "move $3, $0                      \n" //
897                  "sw   $3,     0(%2)                           \n"     // barrier_lock <= 0
898                  "_barrier_init_done:                  \n"
899                  ::"r"(pinit),"r"(pcount),"r"(plock),"r"(value):"$2","$3");
900    return 0 ;
901}
902//////////////////////////////////////////////////////////////////////////////////////
903//      _barrier_wait()
904// This blocking function uses a busy_wait technics (on the barrier_lock value),
905// because the GIET does not support dynamic scheduling/descheduling of tasks.
906// The barrier state is actually defined by two variables:
907// _barrier_count[index] define the number of particpants that are waiting
908// _barrier_lock[index] define the bool variable whose value is polled
909// The last participant change the value of _barrier_lock[index] to release the barrier...
910// There is at most 16 independant barriers, and an error is returned
911// if the barrier index is larger than 15.
912//////////////////////////////////////////////////////////////////////////////////////
913in_drivers int _barrier_wait(unsigned int index)
914{
915    register int*       pcount          = (int*)&_barrier_count[index];         
916    register int        count;
917
918    int                lock             = _barrier_lock[index];         
919
920    if ( index > 15 )   return 1;
921   
922    // parallel decrement _barrier_count[index] using atomic instructions LL/SC
923    // input : pointer on _barrier_count[index]
924    // output : count = _barrier_count[index] (before decrementation)
925    asm volatile ("_barrier_decrement:                          \n"
926                  "ll   %0,     0(%1)                           \n"
927                  "addi $3,     %0,     -1                      \n"
928                  "sc   $3,     0(%1)                           \n"
929                  "beqz $3,     _barrier_decrement              \n"
930                  :"=r"(count):"r"(pcount):"$2","$3");
931
932    // the last task re-initializes the barrier_ count variable
933    // and the barrier_lock variable, waking up all other waiting tasks
934
935    if ( count == 1 )    // last task
936    {
937        _barrier_count[index] = _barrier_initial_value[index];
938        asm volatile( "sync" );
939        _barrier_lock[index]   = (lock == 0) ? 1 : 0;
940        return 0 ;
941    }
942    else                // other tasks
943    {
944        while ( lock == _barrier_lock[index] )  { }     // busy waiting
945        return 0 ;
946    }
947} 
948//////////////////////////////////////////////////////////////////////////////////////
949
950
951// Local Variables:
952// tab-width: 4;
953// c-basic-offset: 4;
954// c-file-offsets:((innamespace . 0)(inline-open . 0));
955// indent-tabs-mode: nil;
956// End:
957//
958// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
959
Note: See TracBrowser for help on using the repository browser.