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

Last change on this file since 248 was 248, checked in by meunier, 10 years ago

Updates in the soft_filter application (bug corrections, formatting, and adaptation to the architecture generic_mmu)

File size: 40.8 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    //tty_address = (char*)(base + tid*TTY_SPAN*4);
379
380    for ( i=0 ; i < length ; i++ )
381    {
382        if((tty_address[TTY_STATUS*4] & 0x2) == 0x2)  break;
383        else
384        {
385            tty_address[TTY_WRITE*4] = buffer[i]; // write character
386            nwritten++;
387        }
388    }
389    return nwritten;
390}
391///////////////////////////////////////////////////////////////////////////////////////
392//  _tty_read()
393// Fetch one character directly from the TTY_READ register of the TTY controler,
394// and writes this character to the user buffer.
395// It doesn't use the TTY_GET_IRQ interrupt and the associated kernel buffer.
396// This is a non blocking call : it returns 0 if the register is empty,
397// and returns 1 if the register is full.
398// It returns -1 in case of error (proc_id or task_id too large or length != 1)
399// The length argument is not used in this implementation, and has been
400// introduced for future implementations.
401///////////////////////////////////////////////////////////////////////////////////////
402in_drivers int _tty_read(char* buffer, int length)
403{
404    char*               tty_address;
405    size_t              ntasks          = (size_t)&NB_TASKS;
406    size_t              nprocs          = (size_t)&NB_PROCS;
407    size_t              nclusters       = (size_t)&NB_CLUSTERS;
408    unsigned int    base                = (unsigned int)&seg_tty_base;
409    unsigned int        increment       = _segment_increment(ntasks*TTY_SPAN*4);
410    size_t              pid             = _procid();
411    size_t                  tid;
412
413    if( pid > 7 )   tid = 0;
414    else            tid = _current_task_array[pid];
415
416    if( length != 1)                        return -1;
417    if( pid >= nprocs*nclusters )   return -1;
418    if( tid >= ntasks )                     return -1;
419   
420    tty_address = (char*)(base + increment + tid*TTY_SPAN*4);
421
422    if((tty_address[TTY_STATUS*4] & 0x1) == 0x1)
423    {
424        buffer[0] = tty_address[TTY_READ*4];
425        return 1;
426    }
427    else
428    {
429        return 0;
430    }
431}
432///////////////////////////////////////////////////////////////////////////////////////
433//  _tty_read_irq()
434// iAS it uses the TTY_GET_IRQ interrupt and the associated kernel buffer,
435// that has been written by the ISR, this function does not access the TTY registers.
436// It fetch one single character from the _tty_get_buf[tty_index] kernel buffer, writes
437// this character to the user buffer, and reset the _tty_get_full[tty_index] buffer.
438// This is a non blocking call : it returns 0 if the kernel buffer is empty,
439// and returns 1 if the buffer is full.
440// It returns -1 in case of error (proc_id or task_id too large, or length != 1)
441// The length argument is not used in this implementation, and has been
442// introduced for future implementations.
443///////////////////////////////////////////////////////////////////////////////////////
444in_drivers int _tty_read_irq(char* buffer, int length)
445{
446    int     pid         = _procid();
447    int     ntasks      = (int)&NB_TASKS;
448    int     nprocs      = (int)&NB_PROCS;
449    int     nclusters   = (int)&NB_CLUSTERS;
450    int     tty_index;
451    int     tid;
452
453    if( pid > 7 )   tid = 0;
454    else            tid = _current_task_array[pid];
455 
456    if( length != 1)                    return -1;
457    if( pid >= nprocs*nclusters )       return -1;
458    if( tid >= ntasks )                 return -1;
459
460    tty_index = pid*ntasks + tid;
461    if( _tty_get_full[tty_index] == 0 ) return 0;
462
463    *buffer = _tty_get_buf[tty_index];
464    _tty_get_full[tty_index] = 0;
465    return 1;
466}
467///////////////////////////////////////////////////////////////////////////////////////
468//  _exit()
469// Exit (suicide) after printing message on  a TTY terminal.
470///////////////////////////////////////////////////////////////////////////////////////
471in_drivers int  _exit()
472{
473    char buf[] = "\n\n!!!  Exit  Processor          !!!\n";
474    int pid = _procid();
475
476    buf[24] = '0';
477    buf[25] = 'x';
478    buf[26] = (char)((pid>>8) & 0xF) + 0x30;
479    buf[27] = (char)((pid>>4) & 0xF) + 0x30;
480    buf[28] = (char)(pid & 0xF)      + 0x30;
481    _tty_write(buf, 36);
482
483    while(1) asm volatile("nop");   // infinite loop...
484}
485
486///////////////////////////////////////////////////////////////////////////////////////
487//  _icu_write()
488// Write a 32 bits word in a memory mapped register of the ICU peripheral
489// The base address is defined by the processor ID
490///////////////////////////////////////////////////////////////////////////////////////
491in_drivers int _icu_write(size_t register_index, int value)
492{
493    int*                icu_address;
494    unsigned int        base = (int)&seg_icu_base;
495    unsigned int        increment = _segment_increment(ICU_SPAN*4);
496
497    if( register_index >= ICU_SPAN ) return -1;
498
499    icu_address = (int*)(base + increment);
500    icu_address[register_index] = value;   // write word
501    return 0;
502}
503///////////////////////////////////////////////////////////////////////////////////////
504//  _icu_read()
505// Read a 32 bits word in a memory mapped register of the ICU peripheral
506// The ICU base address is defined by the processor ID
507///////////////////////////////////////////////////////////////////////////////////////
508in_drivers int _icu_read(size_t register_index, int* buffer)
509{
510    int*                icu_address;
511    unsigned int        base = (int)&seg_icu_base;
512    unsigned int        increment = _segment_increment(ICU_SPAN*4);
513
514    if( register_index >= ICU_SPAN ) return -1;
515
516    icu_address = (int*)(base + increment);
517    *buffer = icu_address[register_index];      // read word
518    return 0;
519}
520///////////////////////////////////////////////////////////////////////////////////////
521//  _gcd_write()
522// Write a 32 bits word in a memory mapped register of the GCD coprocessor
523///////////////////////////////////////////////////////////////////////////////////////
524in_drivers int _gcd_write(size_t register_index, int value)
525{
526    int*    gcd_address;
527    if( register_index >= 4 ) return -1;
528
529    gcd_address = (int*)&seg_gcd_base;
530    gcd_address[register_index] = value;            // write word
531    return 0;
532}
533///////////////////////////////////////////////////////////////////////////////////////
534//  _gcd_read()
535// Read a 32 bits word in a memory mapped register of the GCD coprocessor
536///////////////////////////////////////////////////////////////////////////////////////
537in_drivers int _gcd_read(size_t register_index, int* buffer)
538{
539    int*    gcd_address;
540    if( register_index >= 4 ) return -1;
541
542    gcd_address = (int*)&seg_gcd_base;
543    *buffer = gcd_address[register_index];          // read word
544    return 0;
545}
546///////////////////////////////////////////////////////////////////////////////////////
547//  _locks_write()
548// Release a software spin-lock
549///////////////////////////////////////////////////////////////////////////////////////
550in_drivers int _locks_write(size_t index)
551
552{
553    int     max = (int)&NB_LOCKS;
554    if( index >= max ) return -1;
555
556    _spin_lock[index] = 0;
557    return 0;
558}
559///////////////////////////////////////////////////////////////////////////////////////
560//  _locks_read()
561// Try to take a software spin-lock.
562// This is a blocking call, as there is a busy-waiting loop,
563// until the lock is granted to the requester.
564// There is an internal delay of about 100 cycles between
565// two successive lock read, to avoid bus saturation.
566///////////////////////////////////////////////////////////////////////////////////////
567in_drivers int _locks_read(size_t index)
568{
569    int     max = (int)&NB_LOCKS;
570    if( index >= max ) return -1;
571
572    register int   delay = ((_proctime() +_procid()) & 0xF) << 4;
573    register int * plock = (int *) &_spin_lock[index];                 
574
575    asm volatile ("_locks_llsc:                 \n"
576                  "ll   $2,    0(%0)            \n"     // $2 <= _locks_lock
577                  "bnez $2,    _locks_delay     \n"     // random delay if busy
578                  "li   $3,    1                \n"     // prepare argument for sc 
579                  "sc   $3,    0(%0)            \n"     // try to set _locks_busy
580                  "bnez $3,    _locks_ok        \n"     // exit if atomic
581                  "_locks_delay:                \n"
582                  "move $4,    %1               \n"     // $4 <= delay
583                  "_locks_loop:                 \n"
584                  "addi $4,    $4,    -1        \n"     // $4 <= $4 - 1
585                  "beqz $4,    _locks_loop      \n"     // test end delay
586                  "j           _locks_llsc      \n"     // retry
587                  "_locks_ok:                   \n"
588                  ::"r"(plock),"r"(delay):"$2","$3","$4");
589    return 0;
590}
591//////////////////////////////////////////////////////////////////////////////////////////
592//  I/O BLOCK_DEVICE
593// The three functions below use the three variables _ioc_lock _ioc_done,
594// and _ioc_status for synchronisation.
595// - As the IOC component can be used by several programs running in parallel,
596// the _ioc_lock variable guaranties exclusive access to the device.
597// The _ioc_read() and _ioc_write() functions use atomic LL/SC to get the lock.
598// and set _ioc_lock to a non zero value.
599// The _ioc_write() and _ioc_read() functions are blocking, polling the _ioc_lock
600// variable until the device is available.
601// - When the tranfer is completed, the ISR routine activated by the IOC IRQ
602// set the _ioc_done variable to a non-zero value. Possible address errors detected
603// by the IOC peripheral are reported by the ISR in the _ioc_status variable.
604// The _ioc_completed() function is polling the _ioc_done variable, waiting for
605// tranfer conpletion. When the completion is signaled, the _ioc_completed() function
606// reset the _ioc_done variable to zero, and releases the _ioc_lock variable.
607//
608// In a multi-tasks environment, this polling policy must be replaced by a
609// descheduling policy for the requesting process.
610///////////////////////////////////////////////////////////////////////////////////////
611//  _ioc_get_lock()
612// This blocking function is used by the _ioc_read() and _ioc_write() functions
613// to get _ioc_lock using LL/SC.
614///////////////////////////////////////////////////////////////////////////////////////
615in_drivers void _ioc_get_lock()
616{
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_llsc            \n" // retry  if busy
622                  "li   $3,    1                    \n" // prepare argument for sc 
623                  "sc   $3,    0(%0)                \n" // try to set _ioc_busy
624                  "beqz $3,    _ioc_llsc            \n" // retry if not atomic
625                  ::"r"(plock):"$2","$3");
626}
627//////////////////////////////////////////////////////////////////////////////////////
628//  _ioc_write()
629// Transfer data from a memory buffer to a file on the block_device.
630// - lba    : first block index on the disk
631// - buffer : base address of the memory buffer
632// - count  : number of blocks to be transfered
633// The source buffer must be in user address space.
634///////////////////////////////////////////////////////////////////////////////////////
635in_drivers int _ioc_write(size_t lba, void* buffer, size_t count)
636{
637    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
638
639    // buffer must be in user space
640//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
641//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
642//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
643
644    // get the lock
645    _ioc_get_lock();
646
647    // block_device configuration
648    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
649    ioc_address[BLOCK_DEVICE_COUNT] = count;
650    ioc_address[BLOCK_DEVICE_LBA] = lba;
651    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
652    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
653    return 0;
654}
655///////////////////////////////////////////////////////////////////////////////////////
656//  _ioc_read()
657// Transfer data from a file on the block device to a memory buffer.
658// - lba    : first block index on the disk
659// - buffer : base address of the memory buffer
660// - count  : number of blocks to be transfered
661// The destination buffer must be in user address space.
662// All cache lines corresponding to the the target buffer must be invalidated
663// for cache coherence.
664///////////////////////////////////////////////////////////////////////////////////////
665in_drivers int _ioc_read(size_t lba, void* buffer, size_t count)
666{
667    volatile unsigned int*      ioc_address = (unsigned int*)&seg_ioc_base;
668
669    // buffer must be in user space
670//  size_t block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
671//  if( ( (size_t)buffer + block_size*count ) >= 0x80000000 ) return -1;
672//  if( ( (size_t)buffer                    ) >= 0x80000000 ) return -1;
673
674    // get the lock
675    _ioc_get_lock();
676
677    // block_device configuration
678    ioc_address[BLOCK_DEVICE_BUFFER] = (int)buffer;
679    ioc_address[BLOCK_DEVICE_COUNT] = count;
680    ioc_address[BLOCK_DEVICE_LBA] = lba;
681    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
682    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
683
684    return 0;
685}
686///////////////////////////////////////////////////////////////////////////////////////
687//  _ioc_completed()
688// This blocking function cheks completion of an I/O transfer and reports errors.
689// It returns 0 if the transfer is successfully completed.
690// It returns -1 if an error has been reported.
691///////////////////////////////////////////////////////////////////////////////////////
692in_drivers int _ioc_completed()
693{
694    // waiting for completion
695    while (_ioc_done == 0) { asm volatile("nop"); }
696   
697    // reset synchronisation variables
698    _ioc_done = 0;
699    _ioc_lock = 0;
700
701    // return errors
702    if((_ioc_status != BLOCK_DEVICE_READ_SUCCESS) &&
703            (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS))    return -1;
704    else                                                                    return 0;
705}
706
707//////////////////////////////////////////////////////////////////////////////////////
708//  FRAME_BUFFER
709// The _fb_sync_write & _fb_sync_read functions use a memcpy strategy to implement
710// the transfer between a data buffer (user space) and the frame buffer (kernel space).
711// They are blocking until completion of the transfer.
712//////////////////////////////////////////////////////////////////////////////////////
713//  _fb_sync_write()
714// Transfer data from an user buffer to the frame_buffer device with a memcpy.
715// - offset     : offset (in bytes) in the frame buffer
716// - buffer : base address of the memory buffer
717// - length : number of bytes to be transfered
718//////////////////////////////////////////////////////////////////////////////////////
719in_drivers int  _fb_sync_write(size_t offset, void* buffer, size_t length)
720{
721    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
722    char*       ub = buffer;
723    size_t      i;
724
725    // buffer must be in user space
726//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
727//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
728
729    // memory copy
730    for(i=0 ; i<length ; i++) fb[i] = ub[i];
731    return 0;
732}
733///////////////////////////////////////////////////////////////////////////////////////
734//  _fb_sync_read()
735// Transfer data from the frame_buffer device to an user buffer with a memcpy.
736// - offset     : offset (in bytes) in the frame buffer
737// - buffer : base address of the memory buffer
738// - length : number of bytes to be transfered
739//////////////////////////////////////////////////////////////////////////////////////
740in_drivers int  _fb_sync_read(size_t offset, void* buffer, size_t length)
741{
742    volatile char*  fb = (char*)(void*)&seg_fb_base + offset;
743    char*       ub = buffer;
744    size_t      i;
745
746    // buffer must be in user space
747//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
748//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
749
750    // memory copy
751    for(i=0 ; i<length ; i++) ub[i] = fb[i];
752    return 0;
753}
754//////////////////////////////////////////////////////////////////////////////////////
755// The _fb_write() and _fb_read() functions use the MULTI_DMA
756// coprocessor to transfer data between the user buffer and the frame buffer.
757// The _fb_completed() function, use a polling policy to test
758// the global variables _dma_busy[i] and detect the transfer completion.
759// As each processor can have it's private DMA, there is up to 256 _dma_busy[i]
760// set/reset variables that are indexed by the proc_id.
761// The _dma_busy variable is reset by the ISR associated to the DMA IRQ.
762///////////////////////////////////////////////////////////////////////////////////////
763//  _fb_write()
764// Transfer data from an user buffer to the frame_buffer device using DMA.
765// - offset : offset (in bytes) in the frame buffer
766// - buffer : base address of the memory buffer
767// - length : number of bytes to be transfered
768//////////////////////////////////////////////////////////////////////////////////////
769in_drivers int  _fb_write(size_t offset, void* buffer, size_t length)
770{
771    int*                dma_address;
772    unsigned int        base            = (unsigned int) &seg_dma_base;
773    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
774    char *      fb              = (char *) &seg_fb_base + offset;
775    unsigned int        delay = (_proctime() & 0xF) << 4;
776    unsigned int        pid             = _procid();
777    unsigned int        i;
778
779
780    // checking buffer boundaries (bytes)
781//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
782//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
783
784    // waiting until DMA device is available
785    while (_dma_busy[pid] != 0)
786    {
787        for( i=0 ; i<delay ; i++)   // busy waiting
788        {                           // with a pseudo random
789            asm volatile("nop");    // delay between bus accesses
790        }
791    }
792    _dma_busy[pid] = 1;
793
794    dma_address = (int*)(base + increment);
795 
796    // DMA configuration
797    dma_address[DMA_IRQ_DISABLE] = 0;
798    dma_address[DMA_SRC]        = (int)buffer;
799    dma_address[DMA_DST]        = (int)fb;
800    dma_address[DMA_LEN]        = (int)length;
801    return 0;
802}
803///////////////////////////////////////////////////////////////////////////////////////
804//  _fb_read()
805// Transfer data from the frame_buffer device to an user buffer using DMA.
806// - offset     : offset (in bytes) in the frame buffer
807// - buffer : base address of the memory buffer
808// - length : number of bytes to be transfered
809//////////////////////////////////////////////////////////////////////////////////////
810in_drivers int  _fb_read(size_t offset, void* buffer, size_t length)
811{
812    int*                dma_address;
813    unsigned int        base            = (unsigned int)&seg_dma_base; 
814    unsigned int        increment       = _segment_increment(DMA_SPAN*4);
815    char*               fb              = (char*)&seg_fb_base + offset;
816    unsigned int        delay           = (_proctime() & 0xF) << 4;
817    unsigned int        pid             = _procid();
818    unsigned int        i;
819
820    // checking buffer boundaries (bytes)
821//  if( ( (size_t)buffer + length ) >= 0x80000000 ) return -1;
822//  if( ( (size_t)buffer          ) >= 0x80000000 ) return -1;
823
824    // waiting until DMA device is available
825    while (_dma_busy[pid] != 0)
826    {
827        for( i=0 ; i<delay ; i++)   // busy waiting
828        {                           // with a pseudo random
829            asm volatile("nop");    // delay between bus accesses
830        }
831    }
832    _dma_busy[pid] = 1;
833
834    dma_address = (int*)(base + increment);
835
836    // DMA configuration
837    dma_address[DMA_IRQ_DISABLE] = 0;
838    dma_address[DMA_SRC]        = (int)fb;
839    dma_address[DMA_DST]        = (int)buffer;
840    dma_address[DMA_LEN]        = (int)length;
841    return 0;
842}
843///////////////////////////////////////////////////////////////////////////////////////
844//  _fb_completed()
845// This blocking function cheks completion of a DMA transfer to or fom the frame buffer.
846// The MIPS32 wait instruction stall the processor until the next interrupt.
847// It returns 0 if the transfer is successfully completed
848// It returns -1 if an error has been reported.
849///////////////////////////////////////////////////////////////////////////////////////
850in_drivers int _fb_completed()
851{
852    unsigned int        pid = _procid();
853
854    while (_dma_busy[pid] != 0)
855    {
856        asm volatile("nop");
857    }
858    if(_dma_status[pid] == DMA_SUCCESS)  return 0;
859    else                                 return _dma_status[pid];
860}
861//////////////////////////////////////////////////////////////////////////////////////
862// _barrier_init()
863// This function makes a cooperative initialisation of the barrier:
864// - barrier_count[index] <= N
865// - barrier_lock[index]  <= 0
866// All tasks try to initialize the barrier, but the initialisation
867// is done by only one task, using LL/SC instructions.
868// This cooperative initialisation is questionnable,
869// bcause the barrier can ony be initialised once...
870//////////////////////////////////////////////////////////////////////////////////////
871in_drivers int _barrier_init(unsigned int index, unsigned int value)
872{
873
874    register int* pinit         = (int*)&_barrier_initial_value[index];
875    register int* pcount        = (int*)&_barrier_count[index];
876    register int* plock         = (int*)&_barrier_lock[index];
877
878    if ( index > 7 )    return 1;
879
880    // parallel initialisation using atomic instructions LL/SC
881    asm volatile ("_barrier_init_test:                  \n"
882                  "ll   $2,     0(%0)                   \n"     // read barrier_inital_value
883                  "bnez $2,     _barrier_init_done      \n"
884                  "move $3,     %3                              \n"
885                  "sc   $3,     0(%0)                   \n"     // try to write barrier_initial_value
886                  "beqz $3,     _barrier_init_test      \n"
887                  "move $3,     %3                                  \n" 
888                  "sw   $3,     0(%1)                           \n"     // barrier_count <= barrier_initial_value
889                  "move $3, $0                      \n" //
890                  "sw   $3,     0(%2)                           \n"     // barrier_lock <= 0
891                  "_barrier_init_done:                  \n"
892                  ::"r"(pinit),"r"(pcount),"r"(plock),"r"(value):"$2","$3");
893    return 0 ;
894}
895//////////////////////////////////////////////////////////////////////////////////////
896//      _barrier_wait()
897// This blocking function uses a busy_wait technics (on the barrier_lock value),
898// because the GIET does not support dynamic scheduling/descheduling of tasks.
899// The barrier state is actually defined by two variables:
900// _barrier_count[index] define the number of particpants that are waiting
901// _barrier_lock[index] define the bool variable whose value is polled
902// The last participant change the value of _barrier_lock[index] to release the barrier...
903// There is at most 16 independant barriers, and an error is returned
904// if the barrier index is larger than 15.
905//////////////////////////////////////////////////////////////////////////////////////
906in_drivers int _barrier_wait(unsigned int index)
907{
908    register int*       pcount          = (int*)&_barrier_count[index];         
909    register int        count;
910
911    int                lock             = _barrier_lock[index];         
912
913    if ( index > 15 )   return 1;
914   
915    // parallel decrement _barrier_count[index] using atomic instructions LL/SC
916    // input : pointer on _barrier_count[index]
917    // output : count = _barrier_count[index] (before decrementation)
918    asm volatile ("_barrier_decrement:                          \n"
919                  "ll   %0,     0(%1)                           \n"
920                  "addi $3,     %0,     -1                      \n"
921                  "sc   $3,     0(%1)                           \n"
922                  "beqz $3,     _barrier_decrement              \n"
923                  :"=&r"(count)
924                  :"r"(pcount)
925                  :"$2","$3");
926
927    // the last task re-initializes the barrier_ count variable
928    // and the barrier_lock variable, waking up all other waiting tasks
929
930    if ( count == 1 )    // last task
931    {
932        _barrier_count[index] = _barrier_initial_value[index];
933        asm volatile( "sync" );
934        _barrier_lock[index]   = (lock == 0) ? 1 : 0;
935        return 0 ;
936    }
937    else                // other tasks
938    {
939        while ( lock == _barrier_lock[index] )  { }     // busy waiting
940        return 0 ;
941    }
942} 
943//////////////////////////////////////////////////////////////////////////////////////
944
945
946// Local Variables:
947// tab-width: 4;
948// c-basic-offset: 4;
949// c-file-offsets:((innamespace . 0)(inline-open . 0));
950// indent-tabs-mode: nil;
951// End:
952//
953// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
954
Note: See TracBrowser for help on using the repository browser.