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

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

fixing a bug in _barrier_wait function (thanks to Joel for asm)

File size: 40.8 KB
RevLine 
[158]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
[173]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 };
[158]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
[173]182// No more than 1024 processors...
[158]183////////////////////////////////////////////////////////////////////////////////////////
184in_drivers unsigned int _procid()
185{
186    unsigned int ret;
187    asm volatile( "mfc0 %0, $15, 1": "=r"(ret) );
[173]188    return (ret & 0x3FF);
[158]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;
[173]363    size_t                  nclusters   = (size_t)&NB_CLUSTERS;
364    unsigned int    base                = (unsigned int)&seg_tty_base;
[158]365    unsigned int        increment       = _segment_increment(ntasks*TTY_SPAN*4);
366    size_t              pid             = _procid();
367    int                 nwritten        = 0;
[173]368    size_t                  tid;
[158]369    int                 i;
370
[173]371    if( ntasks == 0 )  tid = 0;
372    else               tid = _current_task_array[pid];
373
374    if( tid >= ntasks )                         return -1;
[158]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;
[173]407    unsigned int    base                = (unsigned int)&seg_tty_base;
[158]408    unsigned int        increment       = _segment_increment(ntasks*TTY_SPAN*4);
409    size_t              pid             = _procid();
[173]410    size_t                  tid;
[158]411
[173]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;
[158]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;
[173]450    int     tid;
[158]451
[173]452    if( pid > 7 )   tid = 0;
453    else            tid = _current_task_array[pid];
454 
[158]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,
[178]593// and _ioc_status for synchronisation.
[158]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*      plock = (unsigned int*)&_ioc_lock;                     
617
[178]618    asm volatile ("_ioc_llsc:                       \n"
[158]619                  "ll   $2,    0(%0)                \n" // $2 <= _ioc_lock
[178]620                  "bnez $2,    _ioc_llsc            \n" // retry  if busy
621                  "li   $3,    1                    \n" // prepare argument for sc 
[158]622                  "sc   $3,    0(%0)                \n" // try to set _ioc_busy
[178]623                  "beqz $3,    _ioc_llsc            \n" // retry if not atomic
624                  ::"r"(plock):"$2","$3");
[158]625}
626//////////////////////////////////////////////////////////////////////////////////////
627//  _ioc_write()
628// Transfer data from a memory buffer to a file on the block_device.
629// - lba    : first block index on the disk
630// - buffer : base address of the memory buffer
631// - count  : number of blocks to be transfered
632// The source buffer must be in user address space.
633///////////////////////////////////////////////////////////////////////////////////////
634in_drivers int _ioc_write(size_t lba, void* buffer, size_t count)
635{
636    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
637
638    // buffer must be in user space
639//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
640//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
641//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
642
643    // get the lock
644    _ioc_get_lock();
645
646    // block_device configuration
647    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
648    ioc_address[BLOCK_DEVICE_COUNT] = count;
649    ioc_address[BLOCK_DEVICE_LBA] = lba;
650    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
651    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
652    return 0;
653}
654///////////////////////////////////////////////////////////////////////////////////////
655//  _ioc_read()
656// Transfer data from a file on the block device to a memory buffer.
657// - lba    : first block index on the disk
658// - buffer : base address of the memory buffer
659// - count  : number of blocks to be transfered
660// The destination buffer must be in user address space.
661// All cache lines corresponding to the the target buffer must be invalidated
662// for cache coherence.
663///////////////////////////////////////////////////////////////////////////////////////
664in_drivers int _ioc_read(size_t lba, void* buffer, size_t count)
665{
666    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
667
668    // buffer must be in user space
669//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
670//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
671//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
672
673    // get the lock
674    _ioc_get_lock();
675
676    // block_device configuration
677    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
678    ioc_address[BLOCK_DEVICE_COUNT] = count;
679    ioc_address[BLOCK_DEVICE_LBA] = lba;
680    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
681    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
682
683    return 0;
684}
685///////////////////////////////////////////////////////////////////////////////////////
686//  _ioc_completed()
687// This blocking function cheks completion of an I/O transfer and reports errors.
688// It returns 0 if the transfer is successfully completed.
689// It returns -1 if an error has been reported.
690///////////////////////////////////////////////////////////////////////////////////////
691in_drivers int _ioc_completed()
692{
693    // waiting for completion
694    while (_ioc_done == 0) { asm volatile("nop"); }
695   
696    // reset synchronisation variables
697    _ioc_done = 0;
698    _ioc_lock = 0;
699
700    // return errors
701    if((_ioc_status != BLOCK_DEVICE_READ_SUCCESS) &&
702            (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS))    return -1;
703    else                                                                    return 0;
704}
705
706//////////////////////////////////////////////////////////////////////////////////////
707//  FRAME_BUFFER
708// The _fb_sync_write & _fb_sync_read functions use a memcpy strategy to implement
709// the transfer between a data buffer (user space) and the frame buffer (kernel space).
710// They are blocking until completion of the transfer.
711//////////////////////////////////////////////////////////////////////////////////////
712//  _fb_sync_write()
713// Transfer data from an user buffer to the frame_buffer device with a memcpy.
714// - offset     : offset (in bytes) in the frame buffer
715// - buffer : base address of the memory buffer
716// - length : number of bytes to be transfered
717//////////////////////////////////////////////////////////////////////////////////////
718in_drivers int  _fb_sync_write(size_t offset, void* buffer, size_t length)
719{
720    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
721    char*       ub = buffer;
722    size_t      i;
723
724    // buffer must be in user space
725//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
726//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
727
728    // memory copy
729    for(i=0 ; i<length ; i++) fb[i] = ub[i];
730    return 0;
731}
732///////////////////////////////////////////////////////////////////////////////////////
733//  _fb_sync_read()
734// Transfer data from the frame_buffer device to an user buffer with a memcpy.
735// - offset     : offset (in bytes) in the frame buffer
736// - buffer : base address of the memory buffer
737// - length : number of bytes to be transfered
738//////////////////////////////////////////////////////////////////////////////////////
739in_drivers int  _fb_sync_read(size_t offset, void* buffer, size_t length)
740{
741    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
742    char*       ub = buffer;
743    size_t      i;
744
745    // buffer must be in user space
746//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
747//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
748
749    // memory copy
750    for(i=0 ; i<length ; i++) ub[i] = fb[i];
751    return 0;
752}
753//////////////////////////////////////////////////////////////////////////////////////
754// The _fb_write() and _fb_read() functions use the MULTI_DMA
755// coprocessor to transfer data between the user buffer and the frame buffer.
756// The _fb_completed() function, use a polling policy to test
757// the global variables _dma_busy[i] and detect the transfer completion.
758// As each processor can have it's private DMA, there is up to 256 _dma_busy[i]
759// set/reset variables that are indexed by the proc_id.
760// The _dma_busy variable is reset by the ISR associated to the DMA IRQ.
761///////////////////////////////////////////////////////////////////////////////////////
762//  _fb_write()
763// Transfer data from an user buffer to the frame_buffer device using DMA.
764// - offset : offset (in bytes) in the frame buffer
765// - buffer : base address of the memory buffer
766// - length : number of bytes to be transfered
767//////////////////////////////////////////////////////////////////////////////////////
768in_drivers int  _fb_write(size_t offset, void* buffer, size_t length)
769{
770    int*                dma_address;
771    unsigned int        base            = (unsigned int)&seg_dma_base;
772    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
773    char*               fb              = (char*)&seg_fb_base + offset;
774    unsigned int        delay           = (_proctime() & 0xF) << 4;
775    unsigned int        pid             = _procid();
776    unsigned int        i;
777
778
779    // checking buffer boundaries (bytes)
780//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
781//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
782
783    // waiting until DMA device is available
784    while (_dma_busy[pid] != 0)
785    {
786        for( i=0 ; i<delay ; i++)   // busy waiting
787        {                           // with a pseudo random
788            asm volatile("nop");    // delay between bus accesses
789        }
790    }
791    _dma_busy[pid] = 1;
792
793    dma_address = (int*)(base + increment);
794 
795    // DMA configuration
796    dma_address[DMA_IRQ_DISABLE] = 0;
797    dma_address[DMA_SRC]        = (int)buffer;
798    dma_address[DMA_DST]        = (int)fb;
799    dma_address[DMA_LEN]        = (int)length;
800    return 0;
801}
802///////////////////////////////////////////////////////////////////////////////////////
803//  _fb_read()
804// Transfer data from the frame_buffer device to an user buffer using DMA.
805// - offset     : offset (in bytes) in the frame buffer
806// - buffer : base address of the memory buffer
807// - length : number of bytes to be transfered
808//////////////////////////////////////////////////////////////////////////////////////
809in_drivers int  _fb_read(size_t offset, void* buffer, size_t length)
810{
811    int*                dma_address;
812    unsigned int        base            = (unsigned int)&seg_dma_base; 
813    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
814    char*               fb              = (char*)&seg_fb_base + offset;
815    unsigned int        delay           = (_proctime() & 0xF) << 4;
816    unsigned int        pid             = _procid();
817    unsigned int        i;
818
819    // checking buffer boundaries (bytes)
820//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
821//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
822
823    // waiting until DMA device is available
824    while (_dma_busy[pid] != 0)
825    {
826        for( i=0 ; i<delay ; i++)   // busy waiting
827        {                           // with a pseudo random
828            asm volatile("nop");    // delay between bus accesses
829        }
830    }
831    _dma_busy[pid] = 1;
832
833    dma_address = (int*)(base + increment);
834
835    // DMA configuration
836    dma_address[DMA_IRQ_DISABLE] = 0;
837    dma_address[DMA_SRC]        = (int)fb;
838    dma_address[DMA_DST]        = (int)buffer;
839    dma_address[DMA_LEN]        = (int)length;
840    return 0;
841}
842///////////////////////////////////////////////////////////////////////////////////////
843//  _fb_completed()
844// This blocking function cheks completion of a DMA transfer to or fom the frame buffer.
845// The MIPS32 wait instruction stall the processor until the next interrupt.
846// It returns 0 if the transfer is successfully completed
847// It returns -1 if an error has been reported.
848///////////////////////////////////////////////////////////////////////////////////////
849in_drivers int _fb_completed()
850{
851    unsigned int        pid = _procid();
852
853    while (_dma_busy[pid] != 0)
854    {
855        asm volatile("nop");
856    }
857    if(_dma_status[pid] == DMA_SUCCESS)  return 0;
858    else                                 return _dma_status[pid];
859}
860//////////////////////////////////////////////////////////////////////////////////////
861// _barrier_init()
862// This function makes a cooperative initialisation of the barrier:
[173]863// - barrier_count[index] <= N
864// - barrier_lock[index]  <= 0
865// All tasks try to initialize the barrier, but the initialisation
[158]866// is done by only one task, using LL/SC instructions.
[173]867// This cooperative initialisation is questionnable,
868// bcause the barrier can ony be initialised once...
[158]869//////////////////////////////////////////////////////////////////////////////////////
870in_drivers int _barrier_init(unsigned int index, unsigned int value)
871{
872
873    register int* pinit         = (int*)&_barrier_initial_value[index];
874    register int* pcount        = (int*)&_barrier_count[index];
[173]875    register int* plock         = (int*)&_barrier_lock[index];
[158]876
877    if ( index > 7 )    return 1;
878
879    // parallel initialisation using atomic instructions LL/SC
880    asm volatile ("_barrier_init_test:                  \n"
[173]881                  "ll   $2,     0(%0)                   \n"     // read barrier_inital_value
[158]882                  "bnez $2,     _barrier_init_done      \n"
[173]883                  "move $3,     %3                              \n"
884                  "sc   $3,     0(%0)                   \n"     // try to write barrier_initial_value
[158]885                  "beqz $3,     _barrier_init_test      \n"
[173]886                  "move $3,     %3                                  \n" 
887                  "sw   $3,     0(%1)                           \n"     // barrier_count <= barrier_initial_value
888                  "move $3, $0                      \n" //
889                  "sw   $3,     0(%2)                           \n"     // barrier_lock <= 0
[158]890                  "_barrier_init_done:                  \n"
[173]891                  ::"r"(pinit),"r"(pcount),"r"(plock),"r"(value):"$2","$3");
[158]892    return 0 ;
893}
894//////////////////////////////////////////////////////////////////////////////////////
895//      _barrier_wait()
[173]896// This blocking function uses a busy_wait technics (on the barrier_lock value),
[158]897// because the GIET does not support dynamic scheduling/descheduling of tasks.
[173]898// The barrier state is actually defined by two variables:
899// _barrier_count[index] define the number of particpants that are waiting
900// _barrier_lock[index] define the bool variable whose value is polled
901// The last participant change the value of _barrier_lock[index] to release the barrier...
902// There is at most 16 independant barriers, and an error is returned
903// if the barrier index is larger than 15.
[158]904//////////////////////////////////////////////////////////////////////////////////////
905in_drivers int _barrier_wait(unsigned int index)
906{
907    register int*       pcount          = (int*)&_barrier_count[index];         
908    register int        count;
909
[173]910    int                lock             = _barrier_lock[index];         
[158]911
[173]912    if ( index > 15 )   return 1;
913   
914    // parallel decrement _barrier_count[index] using atomic instructions LL/SC
915    // input : pointer on _barrier_count[index]
916    // output : count = _barrier_count[index] (before decrementation)
[158]917    asm volatile ("_barrier_decrement:                          \n"
[178]918                  "ll   %0,     0(%1)                           \n"
[158]919                  "addi $3,     %0,     -1                      \n"
[178]920                  "sc   $3,     0(%1)                           \n"
[158]921                  "beqz $3,     _barrier_decrement              \n"
[178]922                  :"=&r"(count)
923                  :"r"(pcount)
924                  :"$2","$3");
[158]925
[173]926    // the last task re-initializes the barrier_ count variable
927    // and the barrier_lock variable, waking up all other waiting tasks
[158]928
929    if ( count == 1 )    // last task
930    {
[173]931        _barrier_count[index] = _barrier_initial_value[index];
932        asm volatile( "sync" );
933        _barrier_lock[index]   = (lock == 0) ? 1 : 0;
934        return 0 ;
[158]935    }
936    else                // other tasks
937    {
[173]938        while ( lock == _barrier_lock[index] )  { }     // busy waiting
[158]939        return 0 ;
940    }
941} 
942//////////////////////////////////////////////////////////////////////////////////////
943
944
945// Local Variables:
946// tab-width: 4;
947// c-basic-offset: 4;
948// c-file-offsets:((innamespace . 0)(inline-open . 0));
949// indent-tabs-mode: nil;
950// End:
951//
952// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
953
Note: See TracBrowser for help on using the repository browser.